git.iko.soy/typeable/pandoc
on master help sign in
Smarter smart quotes.
80e2e88John MacFarlane
src/Text/Pandoc/Parsing.hs+29−16
@@ -105,8 +105,9 @@ module Text.Pandoc.Parsing ( take1WhileP,
105 singleQuoteEnd,
105 singleQuoteEnd,
106 doubleQuoteStart,
106 doubleQuoteStart,
107 doubleQuoteEnd,
107 doubleQuoteEnd,
108− ellipses,
109 apostrophe,
108 apostrophe,
109+ doubleCloseQuote,
110+ ellipses,
110 dash,
111 dash,
111 nested,
112 nested,
112 citeKey,
113 citeKey,
@@ -1398,10 +1399,7 @@ smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, HasQuoteContext
1398 -> ParserT s st m Inlines
1399 -> ParserT s st m Inlines
1399smartPunctuation inlineParser = do
1400smartPunctuation inlineParser = do
1400 guardEnabled Ext_smart
1401 guardEnabled Ext_smart
1401− choice [ quoted inlineParser, apostrophe, dash, ellipses ]
1402+ choice [ quoted inlineParser, apostrophe, doubleCloseQuote, dash, ellipses ]
1402−
1403−apostrophe :: Stream s m Char => ParserT s st m Inlines
1404−apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\x2019")
1405
1403
1406quoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1404quoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1407 => ParserT s st m Inlines
1405 => ParserT s st m Inlines
@@ -1411,16 +1409,22 @@ quoted inlineParser = doubleQuoted inlineParser <|> singleQuoted inlineParser
1411singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1409singleQuoted :: (HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
1412 => ParserT s st m Inlines
1410 => ParserT s st m Inlines
1413 -> ParserT s st m Inlines
1411 -> ParserT s st m Inlines
1414−singleQuoted inlineParser = try $ B.singleQuoted . mconcat
1412+singleQuoted inlineParser = do
1415− <$ singleQuoteStart
1413+ singleQuoteStart
1414+ (B.singleQuoted . mconcat <$>
1415+ try
1416− <*> withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd)
1416+ (withQuoteContext InSingleQuote (many1Till inlineParser singleQuoteEnd)))
1417−
1417+ <|> pure "\8217"
1418+
1418−doubleQuoted :: (HasQuoteContext st m, Stream s m Char)
1419+doubleQuoted :: (HasQuoteContext st m, HasLastStrPosition st, Stream s m Char)
1419 => ParserT s st m Inlines
1420 => ParserT s st m Inlines
1420 -> ParserT s st m Inlines
1421 -> ParserT s st m Inlines
1421−doubleQuoted inlineParser = try $ B.doubleQuoted . mconcat
1422+doubleQuoted inlineParser = do
1422− <$ doubleQuoteStart
1423+ doubleQuoteStart
1424+ (B.doubleQuoted . mconcat <$>
1425+ try
1423− <*> withQuoteContext InDoubleQuote (manyTill inlineParser doubleQuoteEnd)
1426+ (withQuoteContext InDoubleQuote (manyTill inlineParser doubleQuoteEnd)))
1427+ <|> pure (B.str "\8220")
1424
1428
1425failIfInQuoteContext :: (HasQuoteContext st m, Stream s m t)
1429failIfInQuoteContext :: (HasQuoteContext st m, Stream s m t)
1426 => QuoteContext
1430 => QuoteContext
@@ -1443,7 +1447,7 @@ singleQuoteStart = do
1443 guard =<< notAfterString
1447 guard =<< notAfterString
1444 try $ do
1448 try $ do
1445 charOrRef "'\8216\145"
1449 charOrRef "'\8216\145"
1446− notFollowedBy (oneOf [' ', '\t', '\n'])
1450+ notFollowedBy (satisfy isSpaceChar)
1447
1451
1448singleQuoteEnd :: Stream s m Char
1452singleQuoteEnd :: Stream s m Char
1449 => ParserT s st m ()
1453 => ParserT s st m ()
@@ -1451,17 +1455,26 @@ singleQuoteEnd = try $ do
1451 charOrRef "'\8217\146"
1455 charOrRef "'\8217\146"
1452 notFollowedBy alphaNum
1456 notFollowedBy alphaNum
1453
1457
1458+doubleQuoteStart :: (HasLastStrPosition st,
1454−doubleQuoteStart :: (HasQuoteContext st m, Stream s m Char)
1459+ HasQuoteContext st m,
1460+ Stream s m Char)
1455 => ParserT s st m ()
1461 => ParserT s st m ()
1456doubleQuoteStart = do
1462doubleQuoteStart = do
1457 failIfInQuoteContext InDoubleQuote
1463 failIfInQuoteContext InDoubleQuote
1464+ guard =<< notAfterString
1458 try $ do charOrRef "\"\8220\147"
1465 try $ do charOrRef "\"\8220\147"
1459− notFollowedBy (oneOf [' ', '\t', '\n'])
1466+ notFollowedBy (satisfy isSpaceChar)
1460
1467
1461doubleQuoteEnd :: Stream s m Char
1468doubleQuoteEnd :: Stream s m Char
1462 => ParserT s st m ()
1469 => ParserT s st m ()
1463doubleQuoteEnd = void (charOrRef "\"\8221\148")
1470doubleQuoteEnd = void (charOrRef "\"\8221\148")
1464
1471
1472+apostrophe :: Stream s m Char => ParserT s st m Inlines
1473+apostrophe = (char '\'' <|> char '\8217') >> return (B.str "\8217")
1474+
1475+doubleCloseQuote :: Stream s m Char => ParserT s st m Inlines
1476+doubleCloseQuote = B.str "\8221" <$ char '"'
1477+
1465ellipses :: Stream s m Char
1478ellipses :: Stream s m Char
1466 => ParserT s st m Inlines
1479 => ParserT s st m Inlines
1467ellipses = try (string "..." >> return (B.str "\8230"))
1480ellipses = try (string "..." >> return (B.str "\8230"))
src/Text/Pandoc/Readers/Markdown.hs+10−8
@@ -2199,25 +2199,27 @@ citation = try $ do
2199smart :: PandocMonad m => MarkdownParser m (F Inlines)
2199smart :: PandocMonad m => MarkdownParser m (F Inlines)
2200smart = do
2200smart = do
2201 guardEnabled Ext_smart
2201 guardEnabled Ext_smart
2202− doubleQuoted <|> singleQuoted <|>
2202+ doubleQuoted <|> singleQuoted <|> (return <$> doubleCloseQuote) <|>
2203− choice (map (return <$>) [apostrophe, dash, ellipses])
2203+ (return <$> apostrophe) <|> (return <$> dash) <|> (return <$> ellipses)
2204
2204
2205singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2205singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2206−singleQuoted = try $ do
2206+singleQuoted = do
2207 singleQuoteStart
2207 singleQuoteStart
2208− withQuoteContext InSingleQuote $
2208+ (try (withQuoteContext InSingleQuote $
2209 fmap B.singleQuoted . trimInlinesF . mconcat <$>
2209 fmap B.singleQuoted . trimInlinesF . mconcat <$>
2210− many1Till inline singleQuoteEnd
2210+ many1Till inline singleQuoteEnd))
2211+ <|> (return (return (B.str "\8217")))
2211
2212
2212-- doubleQuoted will handle regular double-quoted sections, as well
2213-- doubleQuoted will handle regular double-quoted sections, as well
2213-- as dialogues with an open double-quote without a close double-quote
2214-- as dialogues with an open double-quote without a close double-quote
2214-- in the same paragraph.
2215-- in the same paragraph.
2215doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2216doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
2216−doubleQuoted = try $ do
2217+doubleQuoted = do
2217 doubleQuoteStart
2218 doubleQuoteStart
2218− withQuoteContext InDoubleQuote $
2219+ (try (withQuoteContext InDoubleQuote $
2219 fmap B.doubleQuoted . trimInlinesF . mconcat <$>
2220 fmap B.doubleQuoted . trimInlinesF . mconcat <$>
2220− many1Till inline doubleQuoteEnd
2221+ many1Till inline doubleQuoteEnd))
2222+ <|> (return (return (B.str "\8220")))
2221
2223
2222toRow :: [Blocks] -> Row
2224toRow :: [Blocks] -> Row
2223toRow = Row nullAttr . map B.simpleCell
2225toRow = Row nullAttr . map B.simpleCell
src/Text/Pandoc/Readers/RST.hs+1−18
@@ -1658,21 +1658,4 @@ note = try $ do
1658 return $ B.note contents
1658 return $ B.note contents
1659
1659
1660smart :: PandocMonad m => RSTParser m Inlines
1660smart :: PandocMonad m => RSTParser m Inlines
1661−smart = do
1661+smart = smartPunctuation inline
1662− guardEnabled Ext_smart
1663− doubleQuoted <|> singleQuoted <|>
1664− choice [apostrophe, dash, ellipses]
1665−
1666−singleQuoted :: PandocMonad m => RSTParser m Inlines
1667−singleQuoted = try $ do
1668− singleQuoteStart
1669− withQuoteContext InSingleQuote $
1670− B.singleQuoted . trimInlines . mconcat <$>
1671− many1Till inline singleQuoteEnd
1672−
1673−doubleQuoted :: PandocMonad m => RSTParser m Inlines
1674−doubleQuoted = try $ do
1675− doubleQuoteStart
1676− withQuoteContext InDoubleQuote $
1677− B.doubleQuoted . trimInlines . mconcat <$>
1678− many1Till inline doubleQuoteEnd
src/Text/Pandoc/Readers/TWiki.hs+1−21
@@ -469,27 +469,7 @@ symbol :: PandocMonad m => TWParser m B.Inlines
469symbol = B.str <$> countChar 1 nonspaceChar
469symbol = B.str <$> countChar 1 nonspaceChar
470
470
471smart :: PandocMonad m => TWParser m B.Inlines
471smart :: PandocMonad m => TWParser m B.Inlines
472−smart = do
472+smart = smartPunctuation inline
473− guardEnabled Ext_smart
474− doubleQuoted <|> singleQuoted <|>
475− choice [ apostrophe
476− , dash
477− , ellipses
478− ]
479−
480−singleQuoted :: PandocMonad m => TWParser m B.Inlines
481−singleQuoted = try $ do
482− singleQuoteStart
483− withQuoteContext InSingleQuote
484− (B.singleQuoted . B.trimInlines . mconcat <$> many1Till inline singleQuoteEnd)
485−
486−doubleQuoted :: PandocMonad m => TWParser m B.Inlines
487−doubleQuoted = try $ do
488− doubleQuoteStart
489− contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
490− withQuoteContext InDoubleQuote (doubleQuoteEnd >>
491− return (B.doubleQuoted $ B.trimInlines contents))
492− <|> return (B.str "\8220" B.<> contents)
493
473
494link :: PandocMonad m => TWParser m B.Inlines
474link :: PandocMonad m => TWParser m B.Inlines
495link = try $ do
475link = try $ do
test/Tests/Readers/Markdown.hs+1−1
@@ -358,7 +358,7 @@ tests = [ testGroup "inline code"
358 para (text "The value of the " <> math "x" <> text "\8217s and the systems\8217 condition.")
358 para (text "The value of the " <> math "x" <> text "\8217s and the systems\8217 condition.")
359 , test markdownSmart "unclosed double quote"
359 , test markdownSmart "unclosed double quote"
360 ("**this should \"be bold**"
360 ("**this should \"be bold**"
361− =?> para (strong "this should \"be bold"))
361+ =?> para (strong "this should \8220be bold"))
362 ]
362 ]
363 , testGroup "footnotes"
363 , testGroup "footnotes"
364 [ "indent followed by newline and flush-left text" =:
364 [ "indent followed by newline and flush-left text" =:
test/command/7216.mdnew file+19
@@ -0,0 +1,19 @@
1+```
2+pandoc -t latex
3+"This is some text in quotes. Another paragraph by the same speaker follows. The first paragraph should have no close quote.
4+
5+"The second paragraph should have open and close quotes."
6+
7+| "Open quote on this line,
8+| Close quote on the next line."
9+| "Quotes on the same line."
10+^D
11+``This is some text in quotes. Another paragraph by the same speaker
12+follows. The first paragraph should have no close quote.
13+
14+``The second paragraph should have open and close quotes.''
15+
16+``Open quote on this line,\\
17+Close quote on the next line.''\\
18+``Quotes on the same line.''
19+```
test/command/pandoc-citeproc-320a.md+6−6
@@ -49,17 +49,13 @@ references:
49Foo [@item1; @item2; @item3; @item4; @item5; @item6; @item7; @item8].
49Foo [@item1; @item2; @item3; @item4; @item5; @item6; @item7; @item8].
50^D
50^D
51Foo (al-ʾUdhrī, n.d.; al-ʿUdhrī, n.d.; al-\'Udhrī, n.d.; al-'Udhrī,
51Foo (al-ʾUdhrī, n.d.; al-ʿUdhrī, n.d.; al-\'Udhrī, n.d.; al-'Udhrī,
52−n.d.; al-'Udhrī, n.d.; Uch, n.d.; Uebel, n.d.; Zzz, n.d.).
52+n.d.a, n.d.b; Uch, n.d.; Uebel, n.d.; Zzz, n.d.).
53
53
54::: {#refs .references .csl-bib-body .hanging-indent}
54::: {#refs .references .csl-bib-body .hanging-indent}
55::: {#ref-item6 .csl-entry}
55::: {#ref-item6 .csl-entry}
56Uch, Ann. n.d.
56Uch, Ann. n.d.
57:::
57:::
58
58
59−::: {#ref-item4 .csl-entry}
60−'Udhrī, Jamīl al-. n.d.
61−:::
62−
63::: {#ref-item1 .csl-entry}
59::: {#ref-item1 .csl-entry}
64ʾUdhrī, Jamīl al-. n.d.
60ʾUdhrī, Jamīl al-. n.d.
65:::
61:::
@@ -72,8 +68,12 @@ Uch, Ann. n.d.
72\'Udhrī, Jamīl al-. n.d.
68\'Udhrī, Jamīl al-. n.d.
73:::
69:::
74
70
71+::: {#ref-item4 .csl-entry}
72+'Udhrī, Jamīl al-. n.d.a.
73+:::
74+
75::: {#ref-item5 .csl-entry}
75::: {#ref-item5 .csl-entry}
76−'Udhrī, Jamīl al-. n.d.
76+---------. n.d.b.
77:::
77:::
78
78
79::: {#ref-item7 .csl-entry}
79::: {#ref-item7 .csl-entry}