@@ -23,7 +23,7 @@ module Text.Pandoc.Readers.Markdown (
2323import Control.Monad
2424import Control.Monad.Except (throwError )
2525import Data.Bifunctor (second )
26- import Data.Char (isAlphaNum , isPunctuation , isSpace )
26+ import Data.Char (isAlphaNum , isPunctuation , isSpace , toLower )
2727import Data.List (transpose , elemIndex , sortOn , foldl' )
2828import qualified Data.Map as M
2929import Data.Maybe
@@ -1537,7 +1537,7 @@ inline = do
15371537 ' *' -> strongOrEmph
15381538 ' ^' -> inlineNote <|> superscript <|> blockId -- in this order bc ^[link](/foo)^
15391539 ' [' -> note <|> cite <|> bracketedSpan <|> wikilink B. linkWith <|> link
1540- ' !' -> image
1540+ ' !' -> transclusion <|> image
15411541 ' $' -> math
15421542 ' ~' -> strikeout <|> subscript
15431543 ' =' -> mark
@@ -2042,29 +2042,9 @@ rebasePath pos path = do
20422042 " ." -> path
20432043 d -> T. pack d <> " /" <> path
20442044
2045- image :: PandocMonad m => MarkdownParser m (F Inlines )
2046- image = try $ do
2045+ transclusion :: PandocMonad m => MarkdownParser m (F Inlines )
2046+ transclusion = try $ do
20472047 char ' !'
2048- -- First try wikilink transclusion
2049- (try wikilinkTransclusion) <|>
2050- -- Then try regular image parsing
2051- (do (lab,raw) <- reference
2052- defaultExt <- getOption readerDefaultImageExtension
2053- let constructor attr' src
2054- | " data:" `T.isPrefixOf` src = B. imageWith attr' src -- see #9118
2055- | otherwise =
2056- case takeExtension (T. unpack src) of
2057- " " -> B. imageWith attr' (T. pack $ addExtension (T. unpack src)
2058- $ T. unpack defaultExt)
2059- _ -> B. imageWith attr' src
2060- regLink constructor lab <|> referenceLink constructor (lab, " !" <> raw)) <|>
2061- -- Fallback: if it looks like ![[...]], create a span with literal text
2062- (do try $ string " [["
2063- content <- many1TillChar anyChar (try $ string " ]]" )
2064- return $ return $ B. spanWith (" " , [] , [] ) (B. str $ " ![[" <> content <> " ]]" ))
2065-
2066- wikilinkTransclusion :: PandocMonad m => MarkdownParser m (F Inlines )
2067- wikilinkTransclusion = try $ do
20682048 string " [[" *> notFollowedBy' (char ' [' )
20692049 raw <- many1TillChar anyChar (try $ string " ]]" )
20702050 titleAfter <- (True <$ guardEnabled Ext_wikilinks_title_after_pipe ) <|> pure False
@@ -2075,32 +2055,65 @@ wikilinkTransclusion = try $ do
20752055 | otherwise -> (before, T. drop 1 after)
20762056 let (url, fragment) = T. break (== ' #' ) target'
20772057 guard $ T. all (`notElem` [' \n ' ,' \r ' ,' \f ' ,' \t ' ]) url
2078- if T. null fragment
2079- then do
2080- guardEnabled Ext_wikilink_transclusions
2081- currentDir <- takeDirectory . sourceName <$> getPosition
2082- let filename = T. unpack url <> " .md" -- Assume .md extension for transclusion
2083- -- Support relative paths like "Folder/File" by using currentDir as base
2084- insertIncludedFile parseTranscludedInlines toSources [currentDir] filename Nothing Nothing
2085- else do
2086- let fragmentContent = T. drop 1 fragment -- Remove the '#' prefix
2087- if T. take 1 fragmentContent == " ^"
2088- then do
2089- -- Block ID transclusion: ![[File#^block-id]]
2090- guardEnabled Ext_wikilink_block_transclusions
2091- currentDir <- takeDirectory . sourceName <$> getPosition
2092- let filename = T. unpack url <> " .md" -- Assume .md extension for block transclusion
2093- let blockIdText = T. drop 1 fragmentContent -- Remove the '^' prefix
2094- -- Support relative paths like "Folder/File" by using currentDir as base
2095- insertIncludedFile (parseBlockTransclusion blockIdText) toSources [currentDir] filename Nothing Nothing
2096- else do
2097- -- Heading transclusion: ![[File#Heading]]
2098- guardEnabled Ext_wikilink_heading_transclusions
2099- currentDir <- takeDirectory . sourceName <$> getPosition
2100- let filename = T. unpack url <> " .md" -- Assume .md extension for heading transclusion
2101- let headingText = fragmentContent
2102- -- Support relative paths like "Folder/File" by using currentDir as base
2103- insertIncludedFile (parseHeadingTransclusion headingText) toSources [currentDir] filename Nothing Nothing
2058+
2059+ let urlStr = T. unpack url
2060+ let ext = map toLower $ takeExtension urlStr
2061+ let hasExtension = not (null ext)
2062+ let isMdFile = ext == " .md"
2063+
2064+ -- Check if any transclusion extensions are enabled
2065+ hasTransclusions <- (True <$ guardEnabled Ext_wikilink_transclusions ) <|>
2066+ (True <$ guardEnabled Ext_wikilink_block_transclusions ) <|>
2067+ (True <$ guardEnabled Ext_wikilink_heading_transclusions ) <|>
2068+ return False
2069+
2070+ if hasTransclusions && (not hasExtension || isMdFile)
2071+ then -- Handle as transclusion (no extension or .md extension)
2072+ if T. null fragment
2073+ then do
2074+ guardEnabled Ext_wikilink_transclusions
2075+ currentDir <- takeDirectory . sourceName <$> getPosition
2076+ -- Only add .md extension if the file doesn't already have an extension
2077+ let filename = if null (takeExtension urlStr)
2078+ then T. unpack url <> " .md"
2079+ else T. unpack url
2080+ -- Support relative paths like "Folder/File" by using currentDir as base
2081+ insertIncludedFile parseTranscludedInlines toSources [currentDir] filename Nothing Nothing
2082+ else do
2083+ let fragmentContent = T. drop 1 fragment -- Remove the '#' prefix
2084+ if T. take 1 fragmentContent == " ^"
2085+ then do
2086+ -- Block ID transclusion: ![[File#^block-id]]
2087+ guardEnabled Ext_wikilink_block_transclusions
2088+ currentDir <- takeDirectory . sourceName <$> getPosition
2089+ let filename = T. unpack url <> " .md" -- Assume .md extension for block transclusion
2090+ let blockIdText = T. drop 1 fragmentContent -- Remove the '^' prefix
2091+ -- Support relative paths like "Folder/File" by using currentDir as base
2092+ insertIncludedFile (parseBlockTransclusion blockIdText) toSources [currentDir] filename Nothing Nothing
2093+ else do
2094+ -- Heading transclusion: ![[File#Heading]]
2095+ guardEnabled Ext_wikilink_heading_transclusions
2096+ currentDir <- takeDirectory . sourceName <$> getPosition
2097+ let filename = T. unpack url <> " .md" -- Assume .md extension for heading transclusion
2098+ let headingText = fragmentContent
2099+ -- Support relative paths like "Folder/File" by using currentDir as base
2100+ insertIncludedFile (parseHeadingTransclusion headingText) toSources [currentDir] filename Nothing Nothing
2101+ else -- Handle as image/embed (has non-.md extension or transclusions disabled)
2102+ return $ return $ B. imageWith (" " , [" wikilink" ], [] ) url " " (B. text target')
2103+
2104+ image :: PandocMonad m => MarkdownParser m (F Inlines )
2105+ image = try $ do
2106+ char ' !'
2107+ (lab,raw) <- reference
2108+ defaultExt <- getOption readerDefaultImageExtension
2109+ let constructor attr' src
2110+ | " data:" `T.isPrefixOf` src = B. imageWith attr' src -- see #9118
2111+ | otherwise =
2112+ case takeExtension (T. unpack src) of
2113+ " " -> B. imageWith attr' (T. pack $ addExtension (T. unpack src)
2114+ $ T. unpack defaultExt)
2115+ _ -> B. imageWith attr' src
2116+ regLink constructor lab <|> referenceLink constructor (lab, " !" <> raw)
21042117
21052118note :: PandocMonad m => MarkdownParser m (F Inlines )
21062119note = try $ do
0 commit comments