Shared: avoid Text -> String round-trips in htmlAttrs.

Each attribute was rendered with `text . T.unpack`, converting the
escaped Text to String and back.  Use `literal . fromText` instead
(free for Doc Text, which is what all callers use).  This adds a
FromText constraint to htmlAttrs and tagWithAttrs.

Benchmark (30k-cell table with id/class/data attrs, json ->
mediawiki): 2.15s -> 2.00s with ~70-char attribute values; no change
with short values, where attribute text is a small fraction of the
document.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John MacFarlane
2026-09-21 05:51:17 +00:00
co-authored by Claude
parent 7605eb4ddc
commit 50e596b7d7
+6 -6
View File
@@ -196,24 +196,24 @@ getLang opts meta =
_ -> Nothing
-- | Produce an HTML tag with the given pandoc attributes.
tagWithAttrs :: HasChars a => a -> Attr -> Doc a
tagWithAttrs :: (HasChars a, FromText a) => a -> Attr -> Doc a
tagWithAttrs tag attr = "<" <> literal tag <> (htmlAttrs attr) <> ">"
-- | Produce HTML for the given pandoc attributes, to be used in HTML tags
htmlAttrs :: HasChars a => Attr -> Doc a
htmlAttrs :: (HasChars a, FromText a) => Attr -> Doc a
htmlAttrs (ident, classes, kvs) = addSpaceIfNotEmpty (hsep [
if T.null ident
then empty
else "id=" <> doubleQuotes (text $ T.unpack (escapeStringForXML ident))
else "id=" <> doubleQuotes (literal $ fromText (escapeStringForXML ident))
,if null classes
then empty
else "class=" <> doubleQuotes
(text $ T.unpack . escapeStringForXML $ T.unwords classes)
(literal $ fromText . escapeStringForXML $ T.unwords classes)
,hsep (map (\(k,v) -> formatKey k <> "=" <>
doubleQuotes (text $ T.unpack (escapeStringForXML v))) kvs)
doubleQuotes (literal $ fromText (escapeStringForXML v))) kvs)
])
where
formatKey x = text . T.unpack $
formatKey x = literal . fromText $
if ((x `Set.member` html5Attributes || x `Set.member` rdfaAttributes)
&& x /= "label") -- #10048
|| T.any (== ':') x -- e.g. epub: namespace