git.iko.soy/typeable/pandoc
on master help sign in
Implement curly-brace syntax for Markdown citation keys.
3f09f53John MacFarlane
MANUAL.txt+10−5
@@ -4949,12 +4949,15 @@ Inline and regular footnotes may be mixed freely.
4949#### Extension: `citations` ####
4949#### Extension: `citations` ####
4950
4950
4951Markdown citations go inside square brackets and are separated
4951Markdown citations go inside square brackets and are separated
4952−by semicolons. Each citation must have a key, composed of '@' +
4952+by semicolons. Each citation must have a key and may optionally
4953−the citation identifier from the database, and may optionally
4954−have a prefix, a locator, and a suffix. The citation key must
4953+have a prefix, a locator, and a suffix. The citation key
4954+consists of `@` plus the citation identifier, possibly
4955+enclosed in curly braces. If the identifier starts
4955−begin with a letter, digit, or `_`, and may contain
4956+with a letter, digit, or `_`, followed by zero or more
4956alphanumerics, `_`, and internal punctuation characters
4957alphanumerics, `_`, and internal punctuation characters
4958+(`:.#$%&-+?<>~/`), then the curly braces may be omitted.
4959+Identifiers may not contain whitespace characters or unbalanced
4957−(`:.#$%&-+?<>~/`). Here are some examples:
4960+curly braces. Here are some examples:
4958
4961
4959 Blah blah [see @doe99, pp. 33-35; also @smith04, chap. 1].
4962 Blah blah [see @doe99, pp. 33-35; also @smith04, chap. 1].
4960
4963
@@ -4962,6 +4965,8 @@ alphanumerics, `_`, and internal punctuation characters
4962
4965
4963 Blah blah [@smith04; @doe99].
4966 Blah blah [@smith04; @doe99].
4964
4967
4968+ Blah blah [@{https://example.com/bib?name=foobar&date=2000}, p. 33].
4969+
4965`pandoc` detects locator terms in the [CSL locale files].
4970`pandoc` detects locator terms in the [CSL locale files].
4966Either abbreviated or unabbreviated forms are accepted. In the `en-US`
4971Either abbreviated or unabbreviated forms are accepted. In the `en-US`
4967locale, locator terms can be written in either singular or plural forms,
4972locale, locator terms can be written in either singular or plural forms,
src/Text/Pandoc/Parsing.hs+13−5
@@ -1605,19 +1605,27 @@ nested p = do
1605 return res
1605 return res
1606
1606
1607citeKey :: (Stream s m Char, UpdateSourcePos s Char, HasLastStrPosition st)
1607citeKey :: (Stream s m Char, UpdateSourcePos s Char, HasLastStrPosition st)
1608+ => Bool -- ^ If True, allow expanded @{..} syntax.
1608− => ParserT s st m (Bool, Text)
1609+ -> ParserT s st m (Bool, Text)
1609−citeKey = try $ do
1610+citeKey allowBraced = try $ do
1610 guard =<< notAfterString
1611 guard =<< notAfterString
1611 suppress_author <- option False (True <$ char '-')
1612 suppress_author <- option False (True <$ char '-')
1612 char '@'
1613 char '@'
1614+ key <- simpleCiteIdentifier
1615+ <|> if allowBraced
1616+ then charsInBalanced '{' '}' (satisfy (not . isSpace))
1617+ else mzero
1618+ return (suppress_author, key)
1619+
1620+simpleCiteIdentifier :: (Stream s m Char, UpdateSourcePos s Char)
1621+ => ParserT s st m Text
1622+simpleCiteIdentifier = do
1613 firstChar <- alphaNum <|> char '_' <|> char '*' -- @* for wildcard in nocite
1623 firstChar <- alphaNum <|> char '_' <|> char '*' -- @* for wildcard in nocite
1614 let regchar = satisfy (\c -> isAlphaNum c || c == '_')
1624 let regchar = satisfy (\c -> isAlphaNum c || c == '_')
1615 let internal p = try $ p <* lookAhead regchar
1625 let internal p = try $ p <* lookAhead regchar
1616 rest <- many $ regchar <|> internal (oneOf ":.#$%&-+?<>~/") <|>
1626 rest <- many $ regchar <|> internal (oneOf ":.#$%&-+?<>~/") <|>
1617 try (oneOf ":/" <* lookAhead (char '/'))
1627 try (oneOf ":/" <* lookAhead (char '/'))
1618− let key = firstChar:rest
1619− return (suppress_author, T.pack key)
1628+ return $ T.pack $ firstChar:rest
1620−
1621
1629
1622token :: (Stream s m t)
1630token :: (Stream s m t)
1623 => (t -> Text)
1631 => (t -> Text)
src/Text/Pandoc/Readers/Markdown.hs+3−3
@@ -2094,7 +2094,7 @@ cite = do
2094
2094
2095textualCite :: PandocMonad m => MarkdownParser m (F Inlines)
2095textualCite :: PandocMonad m => MarkdownParser m (F Inlines)
2096textualCite = try $ do
2096textualCite = try $ do
2097− (suppressAuthor, key) <- citeKey
2097+ (suppressAuthor, key) <- citeKey True
2098 -- If this is a reference to an earlier example list item,
2098 -- If this is a reference to an earlier example list item,
2099 -- then don't parse it as a citation. If the example list
2099 -- then don't parse it as a citation. If the example list
2100 -- item comes later, we'll parse it here and figure out in
2100 -- item comes later, we'll parse it here and figure out in
@@ -2174,7 +2174,7 @@ prefix = trimInlinesF . mconcat <$>
2174 manyTill inline (char ']'
2174 manyTill inline (char ']'
2175 <|> lookAhead
2175 <|> lookAhead
2176 (try $ do optional (try (char ';' >> spnl))
2176 (try $ do optional (try (char ';' >> spnl))
2177− citeKey
2177+ citeKey True
2178 return ']'))
2178 return ']'))
2179
2179
2180citeList :: PandocMonad m => MarkdownParser m (F [Citation])
2180citeList :: PandocMonad m => MarkdownParser m (F [Citation])
@@ -2183,7 +2183,7 @@ citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl)
2183citation :: PandocMonad m => MarkdownParser m (F Citation)
2183citation :: PandocMonad m => MarkdownParser m (F Citation)
2184citation = try $ do
2184citation = try $ do
2185 pref <- prefix
2185 pref <- prefix
2186− (suppress_author, key) <- citeKey
2186+ (suppress_author, key) <- citeKey True
2187 suff <- suffix
2187 suff <- suffix
2188 noteNum <- stateNoteNumber <$> getState
2188 noteNum <- stateNoteNumber <$> getState
2189 return $ do
2189 return $ do
src/Text/Pandoc/Readers/Org/Inlines.hs+4−4
@@ -263,7 +263,7 @@ berkeleyCitationList = try $ do
263 where
263 where
264 citationListPart :: PandocMonad m => OrgParser m (F Inlines)
264 citationListPart :: PandocMonad m => OrgParser m (F Inlines)
265 citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do
265 citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do
266− notFollowedBy' citeKey
266+ notFollowedBy' $ citeKey False
267 notFollowedBy (oneOf ";]")
267 notFollowedBy (oneOf ";]")
268 inline
268 inline
269
269
@@ -278,7 +278,7 @@ berkeleyBareTag' = try $ void (string "cite")
278
278
279berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation])
279berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation])
280berkeleyTextualCite = try $ do
280berkeleyTextualCite = try $ do
281− (suppressAuthor, key) <- citeKey
281+ (suppressAuthor, key) <- citeKey False
282 returnF . return $ Citation
282 returnF . return $ Citation
283 { citationId = key
283 { citationId = key
284 , citationPrefix = mempty
284 , citationPrefix = mempty
@@ -351,7 +351,7 @@ citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces)
351citation :: PandocMonad m => OrgParser m (F Citation)
351citation :: PandocMonad m => OrgParser m (F Citation)
352citation = try $ do
352citation = try $ do
353 pref <- prefix
353 pref <- prefix
354− (suppress_author, key) <- citeKey
354+ (suppress_author, key) <- citeKey False
355 suff <- suffix
355 suff <- suffix
356 return $ do
356 return $ do
357 x <- pref
357 x <- pref
@@ -368,7 +368,7 @@ citation = try $ do
368 }
368 }
369 where
369 where
370 prefix = trimInlinesF . mconcat <$>
370 prefix = trimInlinesF . mconcat <$>
371− manyTill inline (char ']' <|> (']' <$ lookAhead citeKey))
371+ manyTill inline (char ']' <|> (']' <$ lookAhead (citeKey False)))
372 suffix = try $ do
372 suffix = try $ do
373 hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
373 hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
374 skipSpaces
374 skipSpaces
src/Text/Pandoc/Writers/Markdown/Inline.hs+7−2
@@ -491,11 +491,16 @@ inlineToMarkdown opts (Cite (c:cs) lst)
491 rest <- mapM convertOne cs
491 rest <- mapM convertOne cs
492 let inbr = suffs <+> joincits rest
492 let inbr = suffs <+> joincits rest
493 br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
493 br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
494− return $ literal ("@" <> citationId c) <+> br
494+ return $ literal ("@" <> maybeInBraces (citationId c)) <+> br
495 else do
495 else do
496 cits <- mapM convertOne (c:cs)
496 cits <- mapM convertOne (c:cs)
497 return $ literal "[" <> joincits cits <> literal "]"
497 return $ literal "[" <> joincits cits <> literal "]"
498 where
498 where
499+ maybeInBraces key =
500+ case readWith (citeKey False >> spaces >> eof)
501+ defaultParserState ("@" <> key) of
502+ Left _ -> "{" <> key <> "}"
503+ Right _ -> key
499 joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
504 joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
500 convertOne Citation { citationId = k
505 convertOne Citation { citationId = k
501 , citationPrefix = pinlines
506 , citationPrefix = pinlines
@@ -504,7 +509,7 @@ inlineToMarkdown opts (Cite (c:cs) lst)
504 = do
509 = do
505 pdoc <- inlineListToMarkdown opts pinlines
510 pdoc <- inlineListToMarkdown opts pinlines
506 sdoc <- inlineListToMarkdown opts sinlines
511 sdoc <- inlineListToMarkdown opts sinlines
507− let k' = literal (modekey m <> "@" <> k)
512+ let k' = literal (modekey m <> "@" <> maybeInBraces k)
508 r = case sinlines of
513 r = case sinlines of
509 Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
514 Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
510 _ -> k' <+> sdoc
515 _ -> k' <+> sdoc
test/command/6026.mdnew file+19
@@ -0,0 +1,19 @@
1+```
2+% pandoc -t native
3+@{https://openreview.net/forum?id=HkwoSDPgg}
4+
5+@https://openreview.net/forum?id=HkwoSDPgg
6+^D
7+[Para [Cite [Citation {citationId = "https://openreview.net/forum?id=HkwoSDPgg", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 1, citationHash = 0}] [Str "@https://openreview.net/forum?id=HkwoSDPgg"]]
8+,Para [Cite [Citation {citationId = "https://openreview.net/forum?id", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 2, citationHash = 0}] [Str "@https://openreview.net/forum?id"],Str "=HkwoSDPgg"]]
9+```
10+```
11+% pandoc -t markdown
12+@{https://openreview.net/forum?id=HkwoSDPgg}
13+
14+@https://openreview.net/forum?id=HkwoSDPgg
15+^D
16+@{https://openreview.net/forum?id=HkwoSDPgg}
17+
18+@https://openreview.net/forum?id=HkwoSDPgg
19+```