Skip to content

Replace highlight.js with static highlighting using Pandoc #437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 37 additions & 63 deletions message-index/css/highlight.css
Original file line number Diff line number Diff line change
@@ -1,67 +1,41 @@
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}

code.hljs {
padding: 3px 5px
}

.hljs {
/* This is based on the output of running Pandoc with the `--standalone` flag and
* `--highlight-style pygments`, with hardcoded colors replaced with CSS
* variables.
*
* The names referred to in the comments are from: https://docs.kde.org/trunk5/en/kate/katepart/highlight.html
* (See header "Available Default Styles".)
*/
div.sourceCode {
color: var(--code-color);
background: var(--code-bg-color);
}

.hljs-comment, .hljs-quote {
color: var(--code-comment-color);
font-style: italic
}

.hljs-doctag, .hljs-formula, .hljs-keyword {
color: var(--code-kw-color);
}

.hljs-deletion, .hljs-name, .hljs-section, .hljs-selector-tag, .hljs-subst {
color: var(--code-name-color);
}

.hljs-literal, .hljs-number {
color: var(--code-literal-color);
}

.hljs-addition, .hljs-attribute, .hljs-meta .hljs-string, .hljs-regexp, .hljs-string {
color: var(--code-string-color);
}

.hljs-type {
color: var(--code-constructor-color);
}

.hljs-attr, .hljs-selector-attr, .hljs-selector-class, .hljs-selector-pseudo, .hljs-template-variable, .hljs-variable {
color: var(--code-attr-color);
}

.hljs-meta {
color: var(--code-pragma-color);
}

.hljs-bullet, .hljs-link, .hljs-selector-id, .hljs-symbol, .hljs-title {
color: var(--code-symbol-color);
}

.hljs-built_in, .hljs-class .hljs-title, .hljs-title.class_ {
color: var(--record-field-color);
}

.hljs-emphasis {
font-style: italic
}

.hljs-strong {
font-weight: 700
}

.hljs-link {
text-decoration: underline
}
code span.al { color: var(); font-weight: bold; } /* Alert */
code span.an { color: var(--code-comment-color); font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: var(--code-pragma-color); } /* Attribute */
code span.bn { color: var(--code-literal-color); } /* BaseN */
code span.bu { color: var(--code-name-color); } /* BuiltIn */
code span.cf { color: var(--code-kw-color); font-weight: bold; } /* ControlFlow */
code span.ch { color: var(--code-literal-color); } /* Char */
code span.cn { color: var(--code-symbol-color); } /* Constant */
code span.co { color: var(--code-comment-color); font-style: italic; } /* Comment */
code span.cv { color: var(--code-comment-color); font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: var(--code-comment-color); font-style: italic; } /* Documentation */
code span.dt { color: var(--code-constructor-color); } /* DataType */
code span.dv { color: var(--code-literal-color); } /* DecVal */
code span.er { color: var(); font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: var(--code-literal-color); } /* Float */
code span.fu { color: var(--code-color); } /* Function */
code span.im { color: var(--code-kw-color); font-weight: bold; } /* Import */
code span.in { color: var(); font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: var(--code-kw-color); font-weight: bold; } /* Keyword */
code span.op { color: var(--code-symbol-color); } /* Operator */
code span.ot { color: var(--code-color); } /* Other */
code span.pp { color: var(--code-comment-color); } /* Preprocessor */
code span.sc { color: var(--code-literal-color); } /* SpecialChar */
code span.ss { color: var(--code-string-color); } /* SpecialString */
code span.st { color: var(--code-string-color); } /* String */
code span.va { color: var(--code-attr-color); } /* Variable */
code span.vs { color: var(--code-string-color); } /* VerbatimString */
code span.wa { color: var(); font-weight: bold; font-style: italic; } /* Warning */
1 change: 1 addition & 0 deletions message-index/message-index.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ executable site
, microlens ^>= 0.4.12
, binary ^>= 0.8.8
, aeson ^>= 2.0.3 || ^>= 2.1
, pandoc ^>= 3.1.3
, pandoc-types ^>= 1.22 || ^>= 1.23
, containers ^>= 0.6
, text ^>= 1.2 || ^>= 2.0
Expand Down
2 changes: 1 addition & 1 deletion message-index/messages/GHC-00158/example1/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ GHC cannot derive an instance for `MyClass`, as it is not stock deriveable. Enab

## Error Message

```haskell
```
NotStockDeriveable.hs:6:12: error: [GHC-00158]
• Can't make a derived instance of ‘MyClass MyType’:
‘MyClass’ is not a stock derivable class (Eq, Show, etc.)
Expand Down
2 changes: 1 addition & 1 deletion message-index/messages/GHC-00158/example2/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ In this example, the `stock` strategy is incorrectly specified when deriving the

## Error Message

```haskell
```
IncorrectDerivingStrategy.hs:6:18: error: [GHC-00158]
• Can't make a derived instance of
‘Num IntWrapper’ with the stock strategy:
Expand Down
2 changes: 1 addition & 1 deletion message-index/messages/GHC-00482/example1/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ When pattern matching with a `case` expression, backslash (`\`) is not required

## Error Message

```haskell
```
LambdaInCase.hs:6:5: error: [GHC-00482]
Lambda-syntax in pattern.
Pattern matching on functions is not possible.
Expand Down
2 changes: 1 addition & 1 deletion message-index/messages/GHC-00482/example2/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Pattern matching on functions is not possible.

## Error Message

```haskell
```
LambdaInPattern.hs:4:4: error: [GHC-00482]
Lambda-syntax in pattern.
Pattern matching on functions is not possible.
Expand Down
48 changes: 37 additions & 11 deletions message-index/site.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)
import Data.Monoid (mappend)
import qualified Data.Text as T
import Data.Traversable
import Debug.Trace
import Hakyll
import Lens.Micro (_1, _2, _3)
import Lens.Micro.Extras (view)
import System.FilePath
import Text.Pandoc.Definition (Meta (..), MetaValue (..), Pandoc (..))
import qualified Text.Pandoc as Pandoc
import qualified Text.Pandoc.Definition as Pandoc

main :: IO ()
main = hakyll $ do
Expand Down Expand Up @@ -67,23 +69,37 @@ main = hakyll $ do
<&> \ident ->
fromFilePath $ takeDirectory (takeDirectory (toFilePath ident)) </> "index.md"
bread <- breadcrumbField ["index.html", thisMessage]

pandocCompiler
>>= loadAndApplyTemplate
"templates/example.html"
( mconcat
[ listField
"files"
( mconcat
[ indexlessUrlField "url",
field "name" (pure . view _1 . itemBody),
-- Set the language that highlight.js should use for syntax highlighting
field "language" $ \(itemBody -> (filename, _, _)) ->
pure $ case dropWhile (== '.') $ takeExtension filename of
"hs" -> "haskell"
other -> other,
field "before" (maybe (pure "<not present>") (fmap itemBody . load . itemIdentifier) . view _2 . itemBody),
field "after" (maybe (pure "<not present>") (fmap itemBody . load . itemIdentifier) . view _3 . itemBody)
]
( let getName = view _1 . itemBody
nameField = field "name" (pure . getName)

highlightField ident lens = field ident $ \item -> do
let name = getName item
case view lens $ itemBody item of
Nothing -> pure "<not present>"
Just exampleItem -> do
exampleText <- fmap itemBody $ load $ itemIdentifier exampleItem
let language =
case takeExtension name of
".hs" -> "haskell"
_ -> ""
pure $ T.unpack $ highlight language $ T.pack $ exampleText

Check notice

Code scanning / HLint

Redundant $

message-index/site.hs:93:81:&nbsp;Suggestion:&nbsp;Redundant&nbsp;$ &nbsp;&nbsp; Found: &nbsp;&nbsp;T.pack&nbsp;$&nbsp;exampleText &nbsp;&nbsp; Perhaps: &nbsp;&nbsp;T.pack&nbsp;exampleText

beforeField = highlightField "beforeHighlighted" _2
afterField = highlightField "afterHighlighted" _3
in [ indexlessUrlField "url",
nameField,
beforeField,
afterField
]
)
)
(return files),
defaultContext
Expand Down Expand Up @@ -287,3 +303,13 @@ indexless url
where
lru = reverse url
toDrop = "index.html"

highlight :: T.Text -> T.Text -> T.Text
highlight language code =
let writerOptions = Pandoc.def
-- We make a fake Pandoc document that's just the code embedded in a code block.
document =
Pandoc.Pandoc mempty [Pandoc.CodeBlock ("", [language], []) code]
in case Pandoc.runPure $ Pandoc.writeHtml5String writerOptions document of
Left err -> error $ "Unexpected Pandoc error: " ++ show err
Right html -> html
5 changes: 0 additions & 5 deletions message-index/templates/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<meta name="robots" content="noindex">
<title>$title$ — Haskell Error Index</title>
<link rel="stylesheet" href="/css/highlight.css">
<script src="/js/highlight.min.js"></script>
<link rel="stylesheet" href="/css/default.css" />
<link rel="stylesheet" href="/css/theme.css" />
</head>
Expand All @@ -35,10 +34,6 @@ <h1>$title$</h1>
<a href="http://jaspervdj.be/hakyll" target="_blank" rel="noopener noreferrer">Hakyll</a>
</p>
</footer>

<script>document.querySelectorAll('code.language-haskell').forEach(el => {
hljs.highlightElement(el);
});</script>
</body>

</html>
10 changes: 6 additions & 4 deletions message-index/templates/example.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
<div class="example">
<div class="example-inner">
<div class="example-title">Before</div>
<!-- keep next line as is, i.e., on one line, or the code will not format properly -->
<pre class="example-pre"><code class="language-$language$">$before$</code></pre>
$beforeHighlighted$
</div>
</div>
<div class="example">
<div class="example-inner">
<div class="example-title">After</div>
<!-- keep next line as is, i.e., on one line, or the code will not format propertly -->
<pre class="example-pre"><code class="language-$language$">$after$</code></pre>
$afterHighlighted$
</div>
</div>
//<script>
// console.log("$name$");
//</script>
</div>
$endfor$