Skip to content

Commit

Permalink
Markdown, CommonMark: add support for wiki links. [API change]
Browse files Browse the repository at this point in the history
Adds the Markdown/CommonMark extensions `wikilinks_title_after_pipe` and
`wikilinks_title_before_pipe`. The former enables links of style `[[Name
of page|Title]]` and the latter `[[Title|Name of page]]`. Titles are
optional in both variants, so this works for both:
`[[https://example.org]]`, `[[Name of page]]`.

The writer is modified to render links with title `wikilink` as a
wikilink if a respective extension is enabled.

Pandoc will use `wikilinks_title_after_pipe` if both extensions are
enabled.

Closes: #2923
  • Loading branch information
tarleb authored and jgm committed Jan 15, 2023
1 parent c71d476 commit ca6d4bf
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 5 deletions.
8 changes: 8 additions & 0 deletions src/Text/Pandoc/Extensions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ data Extension =
| Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$
| Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\]
| Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\]
| Ext_wikilinks_title_after_pipe -- ^ Support wikilinks of style
-- [[target|title]]
| Ext_wikilinks_title_before_pipe -- ^ Support wikilinks of style
-- [[title|target]]
| Ext_xrefs_name -- ^ Use xrefs with names
| Ext_xrefs_number -- ^ Use xrefs with numbers
| Ext_yaml_metadata_block -- ^ YAML metadata block
Expand Down Expand Up @@ -505,6 +509,8 @@ getAllExtensions f = universalExtensions <> getAll f
, Ext_literate_haskell
, Ext_short_subsuperscripts
, Ext_rebase_relative_paths
, Ext_wikilinks_title_after_pipe
, Ext_wikilinks_title_before_pipe
]
getAll "markdown_strict" = allMarkdownExtensions
getAll "markdown_phpextra" = allMarkdownExtensions
Expand Down Expand Up @@ -557,6 +563,8 @@ getAllExtensions f = universalExtensions <> getAll f
, Ext_implicit_header_references
, Ext_attributes
, Ext_sourcepos
, Ext_wikilinks_title_after_pipe
, Ext_wikilinks_title_before_pipe
, Ext_yaml_metadata_block
, Ext_rebase_relative_paths
]
Expand Down
4 changes: 4 additions & 0 deletions src/Text/Pandoc/Readers/CommonMark.hs
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,9 @@ specFor opts = foldr ($) defaultSyntaxSpec exts
[ (footnoteSpec <>) | isEnabled Ext_footnotes opts ] ++
[ (definitionListSpec <>) | isEnabled Ext_definition_lists opts ] ++
[ (taskListSpec <>) | isEnabled Ext_task_lists opts ] ++
[ (wikilinksSpec TitleAfterPipe <>)
| isEnabled Ext_wikilinks_title_after_pipe opts ] ++
[ (wikilinksSpec TitleBeforePipe <>)
| isEnabled Ext_wikilinks_title_before_pipe opts ] ++
[ (rebaseRelativePathsSpec <>)
| isEnabled Ext_rebase_relative_paths opts ]
19 changes: 17 additions & 2 deletions src/Text/Pandoc/Readers/Markdown.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import Control.Monad
import Control.Monad.Except (throwError)
import Data.Bifunctor (second)
import Data.Char (isAlphaNum, isPunctuation, isSpace)
import Text.DocLayout (realLength)
import Data.List (transpose, elemIndex, sortOn, foldl')
import qualified Data.Map as M
import Data.Maybe
Expand All @@ -35,6 +34,7 @@ import qualified Data.ByteString as BS
import System.FilePath (addExtension, takeExtension, takeDirectory)
import qualified System.FilePath.Windows as Windows
import qualified System.FilePath.Posix as Posix
import Text.DocLayout (realLength)
import Text.HTML.TagSoup hiding (Row)
import Text.Pandoc.Builder (Blocks, Inlines)
import qualified Text.Pandoc.Builder as B
Expand Down Expand Up @@ -1516,7 +1516,7 @@ inline = do
'_' -> strongOrEmph
'*' -> strongOrEmph
'^' -> superscript <|> inlineNote -- in this order bc ^[link](/foo)^
'[' -> note <|> cite <|> bracketedSpan <|> link
'[' -> note <|> cite <|> bracketedSpan <|> wikilink <|> link
'!' -> image
'$' -> math
'~' -> strikeout <|> subscript
Expand Down Expand Up @@ -1831,6 +1831,21 @@ source = do
linkTitle :: PandocMonad m => MarkdownParser m Text
linkTitle = quotedTitle '"' <|> quotedTitle '\''

wikilink :: PandocMonad m => MarkdownParser m (F Inlines)
wikilink =
(guardEnabled Ext_wikilinks_title_after_pipe *> wikilink' swap) <|>
(guardEnabled Ext_wikilinks_title_before_pipe *> wikilink' id)
where
swap (a, b) = (b, a)
wikilink' order = try $ do
string "[["
notFollowedBy' (char '[')
raw <- many1TillChar (noneOf "\n\r\f\t") (try $ string "]]")
let (title, url) = case T.break (== '|') raw of
(before, "") -> (before, before)
(before, after) -> order (before, T.drop 1 after)
return . pure . B.link url "wikilink" $ B.str title

link :: PandocMonad m => MarkdownParser m (F Inlines)
link = try $ do
st <- getState
Expand Down
12 changes: 11 additions & 1 deletion src/Text/Pandoc/Writers/Markdown/Inline.hs
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,9 @@ inlineToMarkdown opts lnk@(Link attr@(ident,classes,kvs) txt (src, tit)) = do
case txt of
[Str s] | escapeURI s == srcSuffix -> True
_ -> False
let useWikilink = tit == "wikilink" &&
(isEnabled Ext_wikilinks_title_after_pipe opts ||
isEnabled Ext_wikilinks_title_before_pipe opts)
let useRefLinks = writerReferenceLinks opts && not useAuto
shortcutable <- asks envRefShortcutable
let useShortcutRefLinks = shortcutable &&
Expand All @@ -667,7 +670,14 @@ inlineToMarkdown opts lnk@(Link attr@(ident,classes,kvs) txt (src, tit)) = do
| otherwise -> return $ result <> attrsToMarkua attributes
where result = "[" <> linktext <> "](" <> (literal src) <> ")"
attributes = addKeyValueToAttr attr ("title", tit)
_ | useAuto -> return $ "<" <> literal srcSuffix <> ">"
-- Use wikilinks where possible
_ | src == stringify txt && useWikilink ->
return $ "[[" <> literal (stringify txt) <> "]]"
| useAuto -> return $ "<" <> literal srcSuffix <> ">"
| useWikilink && isEnabled Ext_wikilinks_title_after_pipe opts -> return $
"[[" <> literal src <> "|" <> literal (stringify txt) <> "]]"
| useWikilink && isEnabled Ext_wikilinks_title_before_pipe opts -> return $
"[[" <> literal (stringify txt) <> "|" <> literal src <> "]]"
| useRefLinks ->
let first = "[" <> linktext <> "]"
second = if getKey linktext == getKey reftext
Expand Down
24 changes: 22 additions & 2 deletions test/Tests/Readers/Markdown.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension
Ext_compact_definition_lists pandocExtensions }

markdownGH :: Text -> Pandoc
markdownGH = purely $ readMarkdown def {
readerExtensions = githubMarkdownExtensions }
markdownGH = purely $ readMarkdown def {readerExtensions = enableExtension
Ext_wikilinks_title_before_pipe githubMarkdownExtensions }

markdownMMD :: Text -> Pandoc
markdownMMD = purely $ readMarkdown def {
Expand Down Expand Up @@ -309,6 +309,26 @@ tests = [ testGroup "inline code"
"[https://example.org(](url)" =?>
para (link "url" "" (text "https://example.org("))
]
, testGroup "Github wiki links"
[ test markdownGH "autolink" $
"[[https://example.org]]" =?>
para (link "https://example.org" "wikilink" (str "https://example.org"))
, test markdownGH "link with title" $
"[[title|https://example.org]]" =?>
para (link "https://example.org" "wikilink" (str "title"))
, test markdownGH "bad link with title" $
"[[title|random string]]" =?>
para (link "random string" "wikilink" (str "title"))
, test markdownGH "autolink not being a link" $
"[[Name of page]]" =?>
para (link "Name of page" "wikilink" (str "Name of page"))
, test markdownGH "autolink not being a link with a square bracket" $
"[[Name of ]page]]" =?>
para (link "Name of ]page" "wikilink" (str "Name of ]page"))
, test markdownGH "link with inline start should be a link" $
"[[t`i*t_le|https://example.org]]" =?>
para (link "https://example.org" "wikilink" (str "t`i*t_le"))
]
, testGroup "Headers"
[ "blank line before header" =:
"\n# Header\n"
Expand Down
73 changes: 73 additions & 0 deletions test/command/wikilinks_title_after_pipe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# CommonMark

## Reader
```
% pandoc --from commonmark_x+wikilinks_title_after_pipe -t html --columns 90
[[https://example.org]]
[[https://example.org|title]]
[[name of page]]
[[name of page|title]]
^D
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="name of page" title="wikilink">name of page</a></p>
<p><a href="name of page" title="wikilink">title</a></p>
```

## Writer

```
% pandoc -t commonmark_x+wikilinks_title_after_pipe -f html
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="Home" title="wikilink">Home</a></p>
<p><a href="Name of page" title="wikilink">Title</a></p>
^D
[[https://example.org]]
[[https://example.org|title]]
[[Home]]
[[Name%20of%20page|Title]]
```

# Markdown
## Reader

```
% pandoc --from markdown+wikilinks_title_after_pipe -t html --columns 90
[[https://example.org]]
[[https://example.org|title]]
[[name of page]]
[[name of page|title]]
^D
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="name of page" title="wikilink">name of page</a></p>
<p><a href="name of page" title="wikilink">title</a></p>
```

## Writer

```
% pandoc -t markdown+wikilinks_title_after_pipe -f html
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="Home" title="wikilink">Home</a></p>
<p><a href="Name of page" title="wikilink">Title</a></p>
^D
[[https://example.org]]
[[https://example.org|title]]
[[Home]]
[[Name%20of%20page|Title]]
```
84 changes: 84 additions & 0 deletions test/command/wikilinks_title_before_pipe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# CommonMark

## Reader

```
% pandoc -f commonmark+wikilinks_title_before_pipe -t html --columns 90
[[https://example.org]]
[[title|https://example.org]]
[[Name of page]]
[[Title|Name of page]]
^D
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="Name of page" title="wikilink">Name of page</a></p>
<p><a href="Name of page" title="wikilink">Title</a></p>
```

## Writer

```
% pandoc -t commonmark_x+wikilinks_title_before_pipe -f html
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="Home" title="wikilink">Home</a></p>
<p><a href="Name of page" title="wikilink">Title</a></p>
^D
[[https://example.org]]
[[title|https://example.org]]
[[Home]]
[[Title|Name%20of%20page]]
```

## Regular links should still work

```
% pandoc -f commonmark+wikilinks_title_before_pipe -t html
[Title](Name%20of%20page)
^D
<p><a href="Name%20of%20page">Title</a></p>
```

# Markdown

## Reader

```
% pandoc -f markdown+wikilinks_title_before_pipe -t html --columns 90
[[https://example.org]]
[[title|https://example.org]]
[[Name of page]]
[[Title|Name of page]]
^D
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="Name of page" title="wikilink">Name of page</a></p>
<p><a href="Name of page" title="wikilink">Title</a></p>
```

## Writer

```
% pandoc -t markdown+wikilinks_title_before_pipe -f html
<p><a href="https://example.org" title="wikilink">https://example.org</a></p>
<p><a href="https://example.org" title="wikilink">title</a></p>
<p><a href="Home" title="wikilink">Home</a></p>
<p><a href="Name of page" title="wikilink">Title</a></p>
^D
[[https://example.org]]
[[title|https://example.org]]
[[Home]]
[[Title|Name%20of%20page]]
```

0 comments on commit ca6d4bf

Please sign in to comment.