git.iko.soy/typeable/pandoc
on master help sign in
Add support for reading and writing <kbd> elements

* Text.Pandoc.Shared: export `htmlSpanLikeElements` [API change] This commit also introduces a mapping of HTML span like elements that are internally represented as a Span with a single class, but that are converted back to the original element by the html writer. As of now, only the kbd element is handled this way. Ideally these elements should be handled as plain AST values, but since that would be a breaking change with a large impact, we revert to this stop-gap solution. Fixes https://github.com/jgm/pandoc/issues/5796.

commit1425bf9a6549da00ca87832dc6f19d59269a6a83 authorDaniele D'Orazio <d.dorazio96@gmail.com> dateWed 9 Oct 2019, 10:10 (7 years ago) parenta1977dd
4 files changed+74−26
src/Text/Pandoc/Readers/HTML.hs+9−1 src/Text/Pandoc/Shared.hs+6 src/Text/Pandoc/Writers/HTML.hs+39−25 test/command/5805.md+20
src/Text/Pandoc/Readers/HTML.hs+9−1
@@ -62,7 +62,8 @@ import Text.Pandoc.Options (
6262 extensionEnabled)
6363import Text.Pandoc.Parsing hiding ((<|>))
6464import Text.Pandoc.Shared (addMetaField, blocksToInlines', crFilter, escapeURI,
65− extractSpaces, onlySimpleTableCells, safeRead, underlineSpan)
65+ extractSpaces, htmlSpanLikeElements,
66+ onlySimpleTableCells, safeRead, underlineSpan)
6667import Text.Pandoc.Walk
6768import Text.Parsec.Error
6869import Text.TeXMath (readMathML, writeTeX)
@@ -643,6 +644,7 @@ inline = choice
643644 , pStrong
644645 , pSuperscript
645646 , pSubscript
647+ , pSpanLike
646648 , pSmall
647649 , pStrikeout
648650 , pUnderline
@@ -707,6 +709,12 @@ pSuperscript = pInlinesInTags "sup" B.superscript
707709pSubscript :: PandocMonad m => TagParser m Inlines
708710pSubscript = pInlinesInTags "sub" B.subscript
709711
712+pSpanLike :: PandocMonad m => TagParser m Inlines
713+pSpanLike = Set.foldr
714+ (\tag acc -> acc <|> pInlinesInTags tag (B.spanWith ("",[T.unpack tag],[])))
715+ mzero
716+ htmlSpanLikeElements
717+
710718pSmall :: PandocMonad m => TagParser m Inlines
711719pSmall = pInlinesInTags "small" (B.spanWith ("",["small"],[]))
712720
src/Text/Pandoc/Shared.hs+6
@@ -67,6 +67,7 @@ module Text.Pandoc.Shared (
6767 makeMeta,
6868 eastAsianLineBreakFilter,
6969 underlineSpan,
70+ htmlSpanLikeElements,
7071 splitSentences,
7172 filterIpynbOutput,
7273 -- * TagSoup HTML handling
@@ -694,6 +695,11 @@ eastAsianLineBreakFilter = bottomUp go
694695underlineSpan :: Inlines -> Inlines
695696underlineSpan = B.spanWith ("", ["underline"], [])
696697
698+-- | Set of HTML elements that are represented as Span with a class equal as
699+-- the element tag itself.
700+htmlSpanLikeElements :: Set.Set T.Text
701+htmlSpanLikeElements = Set.fromList [T.pack "kbd"]
702+
697703-- | Returns the first sentence in a list of inlines, and the rest.
698704breakSentence :: [Inline] -> ([Inline], [Inline])
699705breakSentence [] = ([],[])
src/Text/Pandoc/Writers/HTML.hs+39−25
@@ -1,9 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21{-# LANGUAGE CPP #-}
2+{-# LANGUAGE MultiWayIf #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
34{-# LANGUAGE OverloadedStrings #-}
45{-# LANGUAGE ScopedTypeVariables #-}
56{-# LANGUAGE ViewPatterns #-}
6−{-# LANGUAGE MultiWayIf #-}
77{- |
88 Module : Text.Pandoc.Writers.HTML
99 Copyright : Copyright (C) 2006-2019 John MacFarlane
@@ -28,27 +28,28 @@ module Text.Pandoc.Writers.HTML (
2828 writeRevealJs,
2929 tagWithAttributes
3030 ) where
31−import Prelude
3231import Control.Monad.State.Strict
3332import Data.Char (ord, toLower)
3433import Data.List (intercalate, intersperse, isPrefixOf, partition)
34+import Data.List.Split (splitWhen)
3535import Data.Maybe (fromMaybe, isJust, isNothing, mapMaybe)
3636import qualified Data.Set as Set
3737import Data.String (fromString)
38−import Data.List.Split (splitWhen)
3938import Data.Text (Text)
4039import qualified Data.Text as T
4140import qualified Data.Text.Lazy as TL
42−import Text.DocTemplates (FromContext(lookupContext))
4341import Network.HTTP (urlEncode)
4442import Network.URI (URI (..), parseURIReference)
4543import Numeric (showHex)
46−import Text.Blaze.Internal (customLeaf, customParent, MarkupM(Empty))
44+import Prelude
45+import Text.Blaze.Internal (MarkupM (Empty), customLeaf, customParent)
46+import Text.DocTemplates (FromContext (lookupContext))
4747#if MIN_VERSION_blaze_markup(0,6,3)
4848#else
4949import Text.Blaze.Internal (preEscapedString, preEscapedText)
5050#endif
5151import Text.Blaze.Html hiding (contents)
52+import Text.DocTemplates (Context (..))
5253import Text.Pandoc.Definition
5354import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtmlInline, highlight,
5455 styleToCss)
@@ -57,7 +58,6 @@ import Text.Pandoc.Options
5758import Text.Pandoc.Shared
5859import Text.Pandoc.Slides
5960import Text.Pandoc.Templates (renderTemplate)
60−import Text.DocTemplates (Context(..))
6161import Text.Pandoc.Walk
6262import Text.Pandoc.Writers.Math
6363import Text.Pandoc.Writers.Shared
@@ -412,7 +412,7 @@ tableOfContents opts sects = do
412412 _ -> opts
413413 case toTableOfContents opts sects of
414414 bl@(BulletList (_:_)) -> Just <$> blockToHtml opts' bl
415− _ -> return Nothing
415+ _ -> return Nothing
416416
417417-- | Convert list of Note blocks to a footnote <div>.
418418-- Assumes notes are sorted.
@@ -650,7 +650,7 @@ blockToHtml opts (Div (ident, "section":dclasses, dkvs)
650650 isSec (Div _ zs) = any isSec zs
651651 isSec _ = False
652652 let isPause (Para [Str ".",Space,Str ".",Space,Str "."]) = True
653− isPause _ = False
653+ isPause _ = False
654654 let fragmentClass = case slideVariant of
655655 RevealJsSlides -> "fragment"
656656 _ -> "incremental"
@@ -907,9 +907,9 @@ tableRowToHtml :: PandocMonad m
907907tableRowToHtml opts aligns rownum cols' = do
908908 let mkcell = if rownum == 0 then H.th else H.td
909909 let rowclass = case rownum of
910− 0 -> "header"
910+ 0 -> "header"
911911 x | x `rem` 2 == 1 -> "odd"
912− _ -> "even"
912+ _ -> "even"
913913 cols'' <- zipWithM
914914 (\alignment item -> tableItemToHtml opts mkcell alignment item)
915915 aligns cols'
@@ -980,7 +980,7 @@ inlineToHtml :: PandocMonad m
980980inlineToHtml opts inline = do
981981 html5 <- gets stHtml5
982982 case inline of
983− (Str str) -> return $ strToHtml str
983+ (Str str) -> return $ strToHtml str
984984 Space -> return $ strToHtml " "
985985 SoftBreak -> return $ case writerWrapText opts of
986986 WrapNone -> preEscapedString " "
@@ -989,22 +989,36 @@ inlineToHtml opts inline = do
989989 LineBreak -> return $ do
990990 if html5 then H5.br else H.br
991991 strToHtml "\n"
992− (Span (id',classes,kvs) ils)
993− -> inlineListToHtml opts ils >>=
994− addAttrs opts attr' . H.span
995− where attr' = (id',classes',kvs')
996− classes' = filter (`notElem` ["csl-no-emph",
997− "csl-no-strong",
998− "csl-no-smallcaps"]) classes
999− kvs' = if null styles
1000− then kvs
1001− else ("style", concat styles) : kvs
992+
993+ (Span (id',classes,kvs) ils) ->
994+ let spanLikeTag = case classes of
995+ [c] -> do
996+ let c' = T.pack c
997+ guard (c' `Set.member` htmlSpanLikeElements)
998+ pure $ customParent (textTag c')
999+ _ -> Nothing
1000+ in case spanLikeTag of
1001+ Just tag -> tag <$> inlineListToHtml opts ils
1002+ Nothing -> do
1003+ h <- inlineListToHtml opts ils
1004+ addAttrs opts (id',classes',kvs') (H.span h)
1005+ where
10021006 styles = ["font-style:normal;"
1003− | "csl-no-emph" `elem` classes]
1007+ | "csl-no-emph" `elem` classes]
10041008 ++ ["font-weight:normal;"
1005− | "csl-no-strong" `elem` classes]
1009+ | "csl-no-strong" `elem` classes]
10061010 ++ ["font-variant:normal;"
1007− | "csl-no-smallcaps" `elem` classes]
1011+ | "csl-no-smallcaps" `elem` classes]
1012+ kvs' = if null styles
1013+ then kvs
1014+ else ("style", concat styles) : kvs
1015+ classes' = [ c | c <- classes
1016+ , c `notElem` [ "csl-no-emph"
1017+ , "csl-no-strong"
1018+ , "csl-no-smallcaps"
1019+ ]
1020+ ]
1021+
10081022 (Emph lst) -> inlineListToHtml opts lst >>= return . H.em
10091023 (Strong lst) -> inlineListToHtml opts lst >>= return . H.strong
10101024 (Code attr str) -> case hlCode of
test/command/5805.mdnew file+20
@@ -0,0 +1,20 @@
1+```
2+% pandoc -f html -t html
3+<kbd>Ctrl-C</kbd>
4+^D
5+<kbd>Ctrl-C</kbd>
6+```
7+
8+```
9+% pandoc -f html -t native
10+<kbd>Ctrl-C</kbd>
11+^D
12+[Plain [Span ("",["kbd"],[]) [Str "Ctrl-C"]]]
13+```
14+
15+```
16+% pandoc -f native -t html
17+[Plain [Span ("",["kbd"],[]) [Str "Ctrl-C"]]]
18+^D
19+<kbd>Ctrl-C</kbd>
20+```