aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn MacFarlane <[email protected]>2024-10-08 21:58:25 -0700
committerJohn MacFarlane <[email protected]>2024-10-08 21:58:25 -0700
commitbdb1172385422d46243e6b5ede31fb6054fade5f (patch)
tree66c2612fc4bac967b52905b892cc1e66dd13828f
parent0b51580e2d0f0c1b5032198efb31f126d3c62e43 (diff)
Typst writer: make `smart` extension work.
If `smart` is not enabled, a command in the default template will disable smartquote substitutions. When `smart` is enabled, render curly apostrophes as straight and escape straight apostrophes. When `smart` is disabled, render curly apostrophes as curly and don't escape straight apostrophes. And similarly for quotes, em and en dashes. This should give more idiomatic typst output, with fewer unnecessary escapes. Closes #10271.
-rw-r--r--data/templates/default.typst5
-rw-r--r--src/Text/Pandoc/Extensions.hs5
-rw-r--r--src/Text/Pandoc/Writers/Typst.hs32
-rw-r--r--test/command/10271.md29
-rw-r--r--test/command/8966.md4
-rw-r--r--test/tables.typst8
-rw-r--r--test/writer.typst38
7 files changed, 85 insertions, 36 deletions
diff --git a/data/templates/default.typst b/data/templates/default.typst
index 1e7b5efd4..8efe33887 100644
--- a/data/templates/default.typst
+++ b/data/templates/default.typst
@@ -28,6 +28,11 @@ $else$
$template.typst()$
$endif$
+$if(smart)$
+$else$
+#set smartquote(enabled: false)
+
+$endif$
$for(header-includes)$
$header-includes$
diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs
index 868ab9609..7f1b35c17 100644
--- a/src/Text/Pandoc/Extensions.hs
+++ b/src/Text/Pandoc/Extensions.hs
@@ -471,7 +471,8 @@ getDefaultExtensions "jats_articleauthoring" = getDefaultExtensions "jats"
getDefaultExtensions "opml" = pandocExtensions -- affects notes
getDefaultExtensions "markua" = extensionsFromList
[]
-getDefaultExtensions "typst" = extensionsFromList [Ext_citations]
+getDefaultExtensions "typst" = extensionsFromList [Ext_citations,
+ Ext_smart]
getDefaultExtensions "dokuwiki" = extensionsFromList [Ext_smart]
getDefaultExtensions _ = extensionsFromList
[Ext_auto_identifiers]
@@ -656,6 +657,6 @@ getAllExtensions f = universalExtensions <> getAll f
getAll "mediawiki" = autoIdExtensions <>
extensionsFromList
[ Ext_smart ]
- getAll "typst" = extensionsFromList [Ext_citations]
+ getAll "typst" = extensionsFromList [Ext_citations, Ext_smart]
getAll "djot" = extensionsFromList [Ext_sourcepos]
getAll _ = mempty
diff --git a/src/Text/Pandoc/Writers/Typst.hs b/src/Text/Pandoc/Writers/Typst.hs
index 80ae6dab3..42a17dccc 100644
--- a/src/Text/Pandoc/Writers/Typst.hs
+++ b/src/Text/Pandoc/Writers/Typst.hs
@@ -84,6 +84,7 @@ pandocToTypst options (Pandoc meta blocks) = do
Right l ->
resetField "lang" (langLanguage l) .
maybe id (resetField "region") (langRegion l))
+ $ defField "smart" (isEnabled Ext_smart options)
$ defField "toc-depth" (tshow $ writerTOCDepth options)
$ defField "figure-caption-position"
(toPosition $ writerFigureCaptionPosition options)
@@ -351,8 +352,9 @@ inlineToTypst :: PandocMonad m => Inline -> TW m (Doc Text)
inlineToTypst inline =
case inline of
Str txt -> do
+ opts <- gets stOptions
context <- gets stEscapeContext
- return $ escapeTypst context txt
+ return $ escapeTypst (isEnabled Ext_smart opts) context txt
Space -> return space
SoftBreak -> do
wrapText <- gets $ writerWrapText . stOptions
@@ -396,11 +398,17 @@ inlineToTypst inline =
contents <- inlinesToTypst inlines
return $ toTypstTextElement typstTextAttrs contents <> lab
Quoted quoteType inlines -> do
- let q = case quoteType of
- DoubleQuote -> literal "\""
- SingleQuote -> literal "'"
+ opts <- gets stOptions
+ let smart = isEnabled Ext_smart opts
contents <- inlinesToTypst inlines
- return $ q <> contents <> q
+ return $
+ case quoteType of
+ DoubleQuote
+ | smart -> "\"" <> contents <> "\""
+ | otherwise -> "“" <> contents <> "”"
+ SingleQuote
+ | smart -> "'" <> contents <> "'"
+ | otherwise -> "‘" <> contents <> "’"
Cite citations inlines -> do
opts <- gets stOptions
if isEnabled Ext_citations opts
@@ -455,8 +463,8 @@ textstyle s inlines =
, needsEscapeAtLineStart c -> ("\\" <>)
_ -> id
-escapeTypst :: EscapeContext -> Text -> Doc Text
-escapeTypst context t =
+escapeTypst :: Bool -> EscapeContext -> Text -> Doc Text
+escapeTypst smart context t =
(case T.uncons t of
Just (c, _)
| needsEscapeAtLineStart c
@@ -469,9 +477,17 @@ escapeTypst context t =
where
escapeChar c
| c == '\160' = "~"
+ | c == '\8217', smart = "'" -- apostrophe
+ | c == '\8212', smart = "---" -- em dash
+ | c == '\8211', smart = "--" -- en dash
| needsEscape c = "\\" <> T.singleton c
| otherwise = T.singleton c
needsEscape '\160' = True
+ needsEscape '\8217' = smart
+ needsEscape '\8212' = smart
+ needsEscape '\8211' = smart
+ needsEscape '\'' = smart
+ needsEscape '"' = smart
needsEscape '[' = True
needsEscape ']' = True
needsEscape '#' = True
@@ -480,8 +496,6 @@ escapeTypst context t =
needsEscape '@' = True
needsEscape '$' = True
needsEscape '\\' = True
- needsEscape '\'' = True
- needsEscape '"' = True
needsEscape '`' = True
needsEscape '_' = True
needsEscape '*' = True
diff --git a/test/command/10271.md b/test/command/10271.md
new file mode 100644
index 000000000..40f5c0a3e
--- /dev/null
+++ b/test/command/10271.md
@@ -0,0 +1,29 @@
+```
+% pandoc -f markdown+smart -t typst+smart
+"don't do it---"
+^D
+"don't do it---"
+
+```
+```
+% pandoc -f markdown+smart -t typst-smart
+"don't do it---"
+^D
+“don’t do it—”
+
+```
+```
+% pandoc -f markdown-smart -t typst+smart
+"don't do it---"
+^D
+\"don\'t do it---\"
+
+```
+```
+% pandoc -f markdown-smart -t typst-smart
+"don't do it---"
+^D
+"don't do it---"
+
+```
+
diff --git a/test/command/8966.md b/test/command/8966.md
index f0c9e99e1..de70fe720 100644
--- a/test/command/8966.md
+++ b/test/command/8966.md
@@ -4,6 +4,6 @@
^D
#strong[Samsonov T.E.] Shape-Adaptive Geometric Simplification of
Heterogeneous Line Datasets / T. E. Samsonov, O. P. Yakimova \/\/
-International Journal of Geographical Information Science. — 2017. —
-Vol. 31. — № 8. — pp.~1485-1520.
+International Journal of Geographical Information Science. --- 2017. ---
+Vol. 31. --- № 8. --- pp.~1485-1520.
```
diff --git a/test/tables.typst b/test/tables.typst
index 3736a7193..9bdcc13b8 100644
--- a/test/tables.typst
+++ b/test/tables.typst
@@ -55,10 +55,10 @@ Multiline table with caption:
aligned],),
table.hline(),
[First], [row], [12.0], [Example of a row that spans multiple lines.],
- [Second], [row], [5.0], [Here’s another one. Note the blank line between
+ [Second], [row], [5.0], [Here's another one. Note the blank line between
rows.],
)]
- , caption: [Here’s the caption. It may span multiple lines.]
+ , caption: [Here's the caption. It may span multiple lines.]
, kind: table
)
@@ -72,7 +72,7 @@ Multiline table without caption:
aligned],),
table.hline(),
[First], [row], [12.0], [Example of a row that spans multiple lines.],
- [Second], [row], [5.0], [Here’s another one. Note the blank line between
+ [Second], [row], [5.0], [Here's another one. Note the blank line between
rows.],
)]
, kind: table
@@ -98,7 +98,7 @@ Multiline table without column headers:
columns: (15%, 13.75%, 16.25%, 35%),
align: (center,left,right,auto,),
[First], [row], [12.0], [Example of a row that spans multiple lines.],
- [Second], [row], [5.0], [Here’s another one. Note the blank line between
+ [Second], [row], [5.0], [Here's another one. Note the blank line between
rows.],
)]
, kind: table
diff --git a/test/writer.typst b/test/writer.typst
index 7609e0db0..70298b2ed 100644
--- a/test/writer.typst
+++ b/test/writer.typst
@@ -133,7 +133,7 @@
)
-This is a set of tests for pandoc. Most of them are adapted from John Gruber’s
+This is a set of tests for pandoc. Most of them are adapted from John Gruber's
markdown test suite.
#horizontalrule
@@ -164,13 +164,13 @@ with no blank line
= Paragraphs
<paragraphs>
-Here’s a regular paragraph.
+Here's a regular paragraph.
In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item.
Because a hard-wrapped line in the middle of a paragraph looked like a list
item.
-Here’s one with a bullet. \* criminey.
+Here's one with a bullet. \* criminey.
There should be a hard line break \
here.
@@ -320,7 +320,7 @@ Multiple paragraphs:
+ Item 1, graf one.
- Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.
+ Item 1. graf two. The quick brown fox jumped over the lazy dog's back.
+ Item 2.
@@ -332,7 +332,7 @@ Multiple paragraphs:
- Tab
- Tab
-Here’s another:
+Here's another:
+ First
+ Second:
@@ -549,7 +549,7 @@ Interpreted markdown in a table:
This is #emph[emphasized]
And this is #strong[strong]
-Here’s a simple block:
+Here's a simple block:
#block[
foo
@@ -596,7 +596,7 @@ Code:
<hr />
```
-Hr’s:
+Hr's:
#horizontalrule
@@ -637,14 +637,14 @@ a^b c^d, a\~b c\~d.
'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
-'He said, "I want to go."' Were you alive in the 70’s?
+'He said, "I want to go."' Were you alive in the 70's?
Here is some quoted '`code`' and a
"#link("http://example.com/?foo=1&bar=2")[quoted link];".
-Some dashes: one—two — three—four — five.
+Some dashes: one---two --- three---four --- five.
-Dashes between numbers: 5–7, 255–66, 1987–1999.
+Dashes between numbers: 5--7, 255--66, 1987--1999.
Ellipses…and…and….
@@ -658,11 +658,11 @@ Ellipses…and…and….
- $alpha and omega$
- $223$
- $p$-Tree
-- Here’s some display math:
+- Here's some display math:
$ frac(d, d x) f (x) = lim_(h arrow.r 0) frac(f (x + h) - f (x), h) $
-- Here’s one that has a line break in it: $alpha + omega times x^2$.
+- Here's one that has a line break in it: $alpha + omega times x^2$.
-These shouldn’t be math:
+These shouldn't be math:
- To get the famous equation, write `$e = mc^2$`.
- \$22,000 is a #emph[lot] of money. So is \$34,000. (It worked if "lot" is
@@ -670,7 +670,7 @@ These shouldn’t be math:
- Shoes (\$20) and socks (\$5).
- Escaped `$`: \$73 #emph[this should be emphasized] 23\$.
-Here’s a LaTeX table:
+Here's a LaTeX table:
#horizontalrule
@@ -776,14 +776,14 @@ Foo #link("/url/")[biz];.
== With ampersands
<with-ampersands>
-Here’s a
+Here's a
#link("http://example.com/?foo=1&bar=2")[link with an ampersand in the URL];.
-Here’s a link with an amersand in the link text: #link("http://att.com/")[AT&T];.
+Here's a link with an amersand in the link text: #link("http://att.com/")[AT&T];.
-Here’s an #link("/script?foo=1&bar=2")[inline link];.
+Here's an #link("/script?foo=1&bar=2")[inline link];.
-Here’s an #link("/script?foo=1&bar=2")[inline link in pointy braces];.
+Here's an #link("/script?foo=1&bar=2")[inline link in pointy braces];.
== Autolinks
<autolinks>
@@ -825,7 +825,7 @@ Here is a movie #box(image("movie.jpg")) icon.
<footnotes>
Here is a footnote reference,#footnote[Here is the footnote. It can go anywhere
after the footnote reference. It need not be placed at the end of the document.]
-and another.#footnote[Here’s the long note. This one contains multiple blocks.
+and another.#footnote[Here's the long note. This one contains multiple blocks.
Subsequent blocks are indented to show that they belong to the footnote (as with
list items).