diff --git a/datafiles/static/prism-setup.js b/datafiles/static/prism-setup.js
new file mode 100644
index 000000000..3139ce361
--- /dev/null
+++ b/datafiles/static/prism-setup.js
@@ -0,0 +1,6 @@
+window.Prism = { manual: true };
+document.addEventListener('DOMContentLoaded', function () {
+ if (!Prism.plugins || !Prism.plugins.autoloader) return;
+ Prism.plugins.autoloader.languages_path = 'https://cdn.jsdelivr.net/npm/prismjs@1.30.0/components/';
+ Prism.highlightAll();
+});
diff --git a/datafiles/templates/Html/candidate-page.html.st b/datafiles/templates/Html/candidate-page.html.st
index 5952b9650..45b3d2d19 100644
--- a/datafiles/templates/Html/candidate-page.html.st
+++ b/datafiles/templates/Html/candidate-page.html.st
@@ -5,8 +5,7 @@
$endif$
$hackageCssTheme()$
-
-
+ $syntaxHighlighting$
$package.name$$if(package.optional.hasSynopsis)$: $package.optional.synopsis$$endif$
@@ -161,8 +160,6 @@
Readme for $package.name$-$package.version$
[back to package description]
$package.optional.readme$
-
-
$endif$
$if(doc.hasQuickNavV1)$
diff --git a/datafiles/templates/Html/package-page.html.st b/datafiles/templates/Html/package-page.html.st
index 4a42b83c8..d7b8b28e6 100644
--- a/datafiles/templates/Html/package-page.html.st
+++ b/datafiles/templates/Html/package-page.html.st
@@ -5,8 +5,7 @@
$endif$
$hackageCssTheme()$
-
-
+ $syntaxHighlighting$
$package.name$$if(package.optional.hasSynopsis)$: $package.optional.synopsis$$endif$
@@ -302,8 +301,6 @@
[back to package description]
$package.optional.readme$
-
-
$endif$
diff --git a/src/Distribution/Server/Features/PackageCandidates.hs b/src/Distribution/Server/Features/PackageCandidates.hs
index ee82812ce..28ec3ec2a 100644
--- a/src/Distribution/Server/Features/PackageCandidates.hs
+++ b/src/Distribution/Server/Features/PackageCandidates.hs
@@ -35,7 +35,7 @@ import Distribution.Server.Features.Security.Migration
import Distribution.Server.Util.Parse (unpackUTF8)
import Distribution.Server.Util.ServeTarball
import Distribution.Server.Util.Markdown (renderMarkdown, supposedToBeMarkdown)
-import Distribution.Server.Pages.Template (hackagePage)
+import Distribution.Server.Pages.Template (hackagePageWithHead, syntaxHighlightingHead)
import Distribution.Text
import Distribution.Package
@@ -586,8 +586,10 @@ candidatesFeature ServerEnv{serverBlobStore = store}
=<< liftIO (loadTarEntry tarfile offset)
cacheControl [Public, maxAgeDays 30] etag
return $ toResponse $ Resource.XHtml $
- let title = "Changelog for " ++ display (packageId pkg) in
- hackagePage title
+ let title = "Changelog for " ++ display (packageId pkg)
+ headExtra = if supposedToBeMarkdown filename then syntaxHighlightingHead else []
+ in
+ hackagePageWithHead headExtra title
[ XHtml.h2 << title
, XHtml.thediv ! [XHtml.theclass "embedded-author-content"]
<< if supposedToBeMarkdown filename
diff --git a/src/Distribution/Server/Features/PackageContents.hs b/src/Distribution/Server/Features/PackageContents.hs
index 43b2df134..03dddbd17 100644
--- a/src/Distribution/Server/Features/PackageContents.hs
+++ b/src/Distribution/Server/Features/PackageContents.hs
@@ -19,7 +19,7 @@ import Distribution.Server.Features.Users
import Distribution.Server.Util.ServeTarball
import Distribution.Server.Util.Markdown (renderMarkdown, supposedToBeMarkdown)
import Distribution.Server.Util.Parse (unpackUTF8)
-import Distribution.Server.Pages.Template (hackagePage)
+import Distribution.Server.Pages.Template (hackagePageWithHead, syntaxHighlightingHead)
import Distribution.Text
import Distribution.Package
@@ -151,7 +151,8 @@ packageContentsFeature CoreFeature{ coreResource = CoreResource{
return $ toResponse $ Resource.XHtml $
let title = "Changelog for " ++ pkgName
title2 = "Changelog for " XHtml.+++ (XHtml.anchor ! [XHtml.href url] << pkgName)
- in hackagePage title
+ headExtra = if supposedToBeMarkdown filename then syntaxHighlightingHead else []
+ in hackagePageWithHead headExtra title
[ XHtml.h2 << title2
, XHtml.thediv ! [XHtml.theclass "embedded-author-content"]
<< if supposedToBeMarkdown filename
@@ -184,8 +185,10 @@ packageContentsFeature CoreFeature{ coreResource = CoreResource{
=<< liftIO (loadTarEntry tarfile offset)
cacheControl [Public, maxAgeDays 30] etag
return $ toResponse $ Resource.XHtml $
- let title = "Readme for " ++ display (packageId pkg) in
- hackagePage title
+ let title = "Readme for " ++ display (packageId pkg)
+ headExtra = if supposedToBeMarkdown filename then syntaxHighlightingHead else []
+ in
+ hackagePageWithHead headExtra title
[ XHtml.h2 << title
, XHtml.thediv ! [XHtml.theclass "embedded-author-content"]
<< if supposedToBeMarkdown filename
diff --git a/src/Distribution/Server/Pages/PackageFromTemplate.hs b/src/Distribution/Server/Pages/PackageFromTemplate.hs
index d1b919857..71d5e429e 100644
--- a/src/Distribution/Server/Pages/PackageFromTemplate.hs
+++ b/src/Distribution/Server/Pages/PackageFromTemplate.hs
@@ -41,6 +41,7 @@ import Data.Time.Format (defaultTimeLocale, formatTime)
import qualified Data.ByteString.Lazy as BS (ByteString)
import qualified Distribution.Server.Pages.Package as Old
+import Distribution.Server.Pages.Template (syntaxHighlightingHead)
import Data.Time.Clock (UTCTime)
import Distribution.Server.Features.Html.HtmlUtilities
@@ -97,6 +98,7 @@ packagePageTemplate render
-- Miscellaneous things that could still stand to be refactored a bit.
[ "moduleList" $= Old.moduleSection render mdocIndex docURL mPkgId hasQuickNav
, "downloadSection" $= Old.downloadSection render
+ , "syntaxHighlighting" $= readmeSyntaxHighlighting
]
else
-- The main two namespaces
@@ -111,8 +113,15 @@ packagePageTemplate render
, "stability" $= renderStability desc
, "isDeprecated" $= isJust deprs
, "deprecatedMsg" $= deprHtml deprs
+ , "syntaxHighlighting" $= readmeSyntaxHighlighting
]
where
+ readmeSyntaxHighlighting =
+ case (rendReadme render, mreadme) of
+ (Just (_, _, _, filename), Just _)
+ | supposedToBeMarkdown filename -> toHtml syntaxHighlightingHead
+ _ -> noHtml
+
-- Access via "$hackage.varName$"
hackageFieldsTemplate =
if isCandidate
diff --git a/src/Distribution/Server/Pages/Template.hs b/src/Distribution/Server/Pages/Template.hs
index 18096d90e..57818f1d2 100644
--- a/src/Distribution/Server/Pages/Template.hs
+++ b/src/Distribution/Server/Pages/Template.hs
@@ -3,6 +3,7 @@ module Distribution.Server.Pages.Template
( hackagePage
, hackagePageWith
, hackagePageWithHead
+ , syntaxHighlightingHead
) where
import Text.XHtml.Strict
@@ -63,6 +64,36 @@ navigationBar =
]
+-- | Stylesheets and scripts that highlight fenced code blocks in rendered
+-- Markdown.
+syntaxHighlightingHead :: [Html]
+syntaxHighlightingHead =
+ [ cdnStylesheet "themes/prism-solarizedlight.min.css"
+ "sha384-k3ZUhhfN2mU6+BTQVzpopVVQOC5ZJ4WNroBrXNNQqawO4e3kOWFCYCT77lDQupo2"
+ "(prefers-color-scheme: light)"
+ , cdnStylesheet "themes/prism-tomorrow.min.css"
+ "sha384-wFjoQjtV1y5jVHbt0p35Ui8aV8GVpEZkyF99OXWqP/eNJDU93D3Ugxkoyh6Y2I4A"
+ "(prefers-color-scheme: dark)"
+ , (script noHtml) ! [strAttr "defer" "defer", src "/static/prism-setup.js"]
+ , cdnScript "components/prism-core.min.js"
+ "sha384-zLRFO4dwowZvh8kzutOb5AWhH7f39HeJp+N7PtHF1SQtTBnifRx0AtmvTYs3F4YV"
+ , cdnScript "plugins/autoloader/prism-autoloader.min.js"
+ "sha384-Uq05+JLko69eOiPr39ta9bh7kld5PKZoU+fF7g0EXTAriEollhZ+DrN8Q/Oi8J2Q"
+ ]
+ where
+ prismURL path = "https://cdn.jsdelivr.net/npm/prismjs@1.30.0/" ++ path
+ cdnStylesheet path hash media =
+ thelink ! [ rel "stylesheet"
+ , href (prismURL path)
+ , strAttr "integrity" hash
+ , strAttr "crossorigin" "anonymous"
+ , strAttr "media" media] << noHtml
+ cdnScript path hash =
+ (script noHtml) ! [ strAttr "defer" "defer"
+ , src (prismURL path)
+ , strAttr "integrity" hash
+ , strAttr "crossorigin" "anonymous"]
+
googleFontURL :: URL
googleFontURL = "https://fonts.googleapis.com/css?family=PT+Sans:400,400i,700"