From e344dcb6adf1272c778d62d01edcaaa619796d23 Mon Sep 17 00:00:00 2001 From: Facundo Olano Date: Tue, 27 Feb 2024 08:45:29 -0300 Subject: [PATCH] Syntax highlighting (#15) * update markdown and org renderers to use chrome highlighting * add example code blocks * fix missing nav in default layout * remove problematic code nowrap * add language to tutorial samples * set theme explicitly * allow setting highlight theme with config * fix config key for hl * try to handle both light dark settings gracefully * fix some problematic css color settings * fix missing raw template * fix weird chroma code height artifacts * improve tabler rendering in mobile * remove redundant default --- commands/initfiles/layouts/post.html | 1 + commands/initfiles/src/assets/css/main.css | 65 +++++++++++++++---- .../initfiles/src/blog/goodbye-markdown.md | 12 ++++ commands/initfiles/src/blog/hello-org.org | 12 ++++ config/config.go | 36 +++++----- docs/src/assets/css/main.css | 65 +++++++++++++++---- docs/src/blog/goodbye-markdown.md | 12 ++++ docs/src/blog/hello-org.org | 12 ++++ docs/src/tutorial/jorge-build.org | 6 +- docs/src/tutorial/jorge-init.org | 9 ++- docs/src/tutorial/jorge-post.org | 10 +-- docs/src/tutorial/jorge-serve.org | 8 ++- go.mod | 3 + go.sum | 22 +++++-- markup/templates.go | 41 +++++++++++- markup/templates_test.go | 8 +-- site/site.go | 6 +- 17 files changed, 260 insertions(+), 68 deletions(-) diff --git a/commands/initfiles/layouts/post.html b/commands/initfiles/layouts/post.html index 1bf25bc..4351dbd 100644 --- a/commands/initfiles/layouts/post.html +++ b/commands/initfiles/layouts/post.html @@ -1,6 +1,7 @@ --- layout: base --- +{% include nav.html %}
diff --git a/commands/initfiles/src/assets/css/main.css b/commands/initfiles/src/assets/css/main.css index c23a4af..8eb85af 100644 --- a/commands/initfiles/src/assets/css/main.css +++ b/commands/initfiles/src/assets/css/main.css @@ -60,9 +60,14 @@ a:hover { } a.title { - color: unset!important; + color: black; padding-right: .25rem; } +@media (prefers-color-scheme: dark) { + a.title { + color: white; + } +} .footer, .footer a, .date, .tags, .tags a { color: silver; @@ -129,7 +134,8 @@ img.cover-img { margin-top: -0.5rem; } -.src pre { +pre { + font-size: 1rem; overflow-x: auto; line-height: 1.5; padding: 1rem; @@ -137,21 +143,52 @@ img.cover-img { scrollbar-width: none; /* Firefox */ } -.src pre, code { - background: whitesmoke; - font-size: 1rem; - hyphens: none; -} - -code { - white-space: nowrap; -} - /* Hide scrollbar for Chrome, Safari and Opera */ .src pre::-webkit-scrollbar { display: none; } +/* +hack to fix height weirdness in code snippets +https://github.com/alecthomas/chroma/issues/617#issuecomment-1063251066 +*/ +code > span[style^="display:flex;"] { + display: inline !important; +} + +/* There are several code rendering scenarios that need to be handled reasonably: +- light vs dark preferred color scheme +- pre code blocks vs inline code spans +- language set vs no language set +- markdown vs org rendering + +Need to decide between: +- fixing the color scheme of the page + using one of chroma's themes +- supporting both light/dark and disabling syntax highlighting altogether +- supporting light/dark, picking a theme and patching it to render decently + with both preferences (as done below, assuming chroma's github theme) +*/ +.layout-post :not(pre) > code { + padding: .2em .4em; + background-color: whitesmoke!important; + color: black; +} + +@media (prefers-color-scheme: dark) { + .layout-post :not(pre) > code { + background-color: rgba(110, 118, 129, 0.4)!important; + color: white; + } +} + +.layout-post pre, .layout-post code { + border-radius: 6px; + background-color: whitesmoke!important; + font-size: 1rem; + hyphens: none; + color: black; +} + blockquote { border-left: 2px solid whitesmoke; padding-left: 1rem; @@ -208,6 +245,10 @@ table tr { border-bottom: 1px solid #ddd; } +@media screen and (max-width: 480px) { + td { display: inline-block } +} + td, th { padding: .5rem; } diff --git a/commands/initfiles/src/blog/goodbye-markdown.md b/commands/initfiles/src/blog/goodbye-markdown.md index 60509d3..6c83b06 100644 --- a/commands/initfiles/src/blog/goodbye-markdown.md +++ b/commands/initfiles/src/blog/goodbye-markdown.md @@ -10,4 +10,16 @@ layout: post For the record, even though it has *org* in the name, jorge can also render markdown, thanks to [goldmark](https://github.com/yuin/goldmark/). +Let's look at some code: + +``` python +import os + +def hello(): + print("Hello World!") + os.exit(0) + +hello() +``` + [Next time](./hello-org), I'll talk about org-mode posts. diff --git a/commands/initfiles/src/blog/hello-org.org b/commands/initfiles/src/blog/hello-org.org index 456bca2..73420e2 100644 --- a/commands/initfiles/src/blog/hello-org.org +++ b/commands/initfiles/src/blog/hello-org.org @@ -15,6 +15,18 @@ As you can see, /italics/ and *bold* render as expected, and you can even use fo All of this is powered by [[https://github.com/niklasfasching/go-org][go-org]], btw[fn:2]. +Let's look at some code: + +#+begin_src python +import os + +def hello(): + print("Hello World!") + os.exit(0) + +hello() +#+end_src + ** Notes [fn:1] See? diff --git a/config/config.go b/config/config.go index 17f49bb..ae46a65 100644 --- a/config/config.go +++ b/config/config.go @@ -26,9 +26,10 @@ type Config struct { IncludesDir string DataDir string - SiteUrl string - PostFormat string - Lang string + SiteUrl string + PostFormat string + Lang string + HighlightTheme string Minify bool LiveReload bool @@ -48,18 +49,19 @@ func Load(rootDir string) (*Config, error) { // TODO allow to disable minify config := &Config{ - RootDir: rootDir, - SrcDir: filepath.Join(rootDir, "src"), - TargetDir: filepath.Join(rootDir, "target"), - LayoutsDir: filepath.Join(rootDir, "layouts"), - IncludesDir: filepath.Join(rootDir, "includes"), - DataDir: filepath.Join(rootDir, "data"), - PostFormat: "blog/:title.org", - Lang: "en", - Minify: true, - LiveReload: false, - LinkStatic: false, - pageDefaults: map[string]interface{}{}, + RootDir: rootDir, + SrcDir: filepath.Join(rootDir, "src"), + TargetDir: filepath.Join(rootDir, "target"), + LayoutsDir: filepath.Join(rootDir, "layouts"), + IncludesDir: filepath.Join(rootDir, "includes"), + DataDir: filepath.Join(rootDir, "data"), + PostFormat: "blog/:title.org", + Lang: "en", + HighlightTheme: "github", + Minify: true, + LiveReload: false, + LinkStatic: false, + pageDefaults: map[string]interface{}{}, } // load overrides from config.yml @@ -79,6 +81,7 @@ func Load(rootDir string) (*Config, error) { } // set user-provided overrides of declared config keys + // TODO less copypasty way of declaring config overrides if url, found := config.overrides["url"]; found { config.SiteUrl = url.(string) } @@ -88,6 +91,9 @@ func Load(rootDir string) (*Config, error) { if format, found := config.overrides["lang"]; found { config.Lang = format.(string) } + if format, found := config.overrides["highlight_theme"]; found { + config.HighlightTheme = format.(string) + } return config, nil } diff --git a/docs/src/assets/css/main.css b/docs/src/assets/css/main.css index d1b1dc3..c2aecd7 100644 --- a/docs/src/assets/css/main.css +++ b/docs/src/assets/css/main.css @@ -60,9 +60,14 @@ a:hover { } a.title { - color: unset!important; + color: black; padding-right: .25rem; } +@media (prefers-color-scheme: dark) { + a.title { + color: white; + } +} .footer, .footer a, .date, .tags, .tags a { color: silver; @@ -129,7 +134,7 @@ img.cover-img { margin-top: -0.5rem; } -.src pre { +pre { font-size: 1rem; overflow-x: auto; line-height: 1.5; @@ -138,21 +143,52 @@ img.cover-img { scrollbar-width: none; /* Firefox */ } -.layout-post .src pre, .layout-post code { - background: whitesmoke; - font-size: 1rem; - hyphens: none; -} - -code { - white-space: nowrap; -} - /* Hide scrollbar for Chrome, Safari and Opera */ .src pre::-webkit-scrollbar { display: none; } +/* +hack to fix height weirdness in code snippets +https://github.com/alecthomas/chroma/issues/617#issuecomment-1063251066 +*/ +code > span[style^="display:flex;"] { + display: inline !important; +} + +/* There are several code rendering scenarios that need to be handled reasonably: +- light vs dark preferred color scheme +- pre code blocks vs inline code spans +- language set vs no language set +- markdown vs org rendering + +Need to decide between: +- fixing the color scheme of the page + using one of chroma's themes +- supporting both light/dark and disabling syntax highlighting altogether +- supporting light/dark, picking a theme and patching it to render decently + with both preferences (as done below, assuming chroma's github theme) +*/ +.layout-post :not(pre) > code { + padding: .2em .4em; + background-color: whitesmoke!important; + color: black +} + +@media (prefers-color-scheme: dark) { + .layout-post :not(pre) > code { + background-color: rgba(110, 118, 129, 0.4)!important; + color: white; + } +} + +.layout-post pre, .layout-post code { + border-radius: 6px; + background-color: whitesmoke!important; + font-size: 1rem; + hyphens: none; + color: black; +} + blockquote { border-left: 2px solid whitesmoke; padding-left: 1rem; @@ -209,6 +245,11 @@ table tr { border-bottom: 1px solid #ddd; } +@media screen and (max-width: 480px) { + td { display: inline-block } +} + + td, th { padding: .5rem; } diff --git a/docs/src/blog/goodbye-markdown.md b/docs/src/blog/goodbye-markdown.md index 60509d3..6c83b06 100644 --- a/docs/src/blog/goodbye-markdown.md +++ b/docs/src/blog/goodbye-markdown.md @@ -10,4 +10,16 @@ layout: post For the record, even though it has *org* in the name, jorge can also render markdown, thanks to [goldmark](https://github.com/yuin/goldmark/). +Let's look at some code: + +``` python +import os + +def hello(): + print("Hello World!") + os.exit(0) + +hello() +``` + [Next time](./hello-org), I'll talk about org-mode posts. diff --git a/docs/src/blog/hello-org.org b/docs/src/blog/hello-org.org index 456bca2..73420e2 100644 --- a/docs/src/blog/hello-org.org +++ b/docs/src/blog/hello-org.org @@ -15,6 +15,18 @@ As you can see, /italics/ and *bold* render as expected, and you can even use fo All of this is powered by [[https://github.com/niklasfasching/go-org][go-org]], btw[fn:2]. +Let's look at some code: + +#+begin_src python +import os + +def hello(): + print("Hello World!") + os.exit(0) + +hello() +#+end_src + ** Notes [fn:1] See? diff --git a/docs/src/tutorial/jorge-build.org b/docs/src/tutorial/jorge-build.org index c1dc07f..226092f 100755 --- a/docs/src/tutorial/jorge-build.org +++ b/docs/src/tutorial/jorge-build.org @@ -11,7 +11,7 @@ index: 5 So far you've seen how to [[file:jorge-init][start a project]], [[file:jorge-serve][serve it locally]] and [[file:jorge-post][add some content]] to it. The last part of job is to prepare your site for the public, and ~jorge build~ will help with that: -#+begin_src +#+begin_src console $ jorge build wrote target/2024-02-23-another-kind-of-post.html wrote target/blog/my-own-blog-post.html @@ -34,7 +34,7 @@ Once you have your static site rendered, it's just a matter to putting it out in But for the sake of completenss, this is how this site is deployed: I have a VPS box running Debian Linux and with the [[https://www.nginx.com/][nginx]] server installed on it. I added this configuration to ~/etc/nginx/sites-enabled/jorge~: -#+begin_src +#+begin_src nginx server { charset utf-8; root /var/www/jorge; @@ -52,7 +52,7 @@ I instructed my DNS provider to point ~jorge.olano.dev~ to the box and I ran [[h I then created the ~/var/www/jorge~ directory in the server, and deployed from my laptop using ~rsync~: -#+begin_src +#+begin_src console $ jorge build $ rsync -vPrz --delete target/ root@olano.dev:/var/www/jorge #+end_src diff --git a/docs/src/tutorial/jorge-init.org b/docs/src/tutorial/jorge-init.org index ddc5c23..4621b90 100755 --- a/docs/src/tutorial/jorge-init.org +++ b/docs/src/tutorial/jorge-init.org @@ -11,12 +11,11 @@ index: 2 Once jorge is [[file:installation][installed]], you can start a new site with the ~init~ command, specifying a project directory (~myblog~ in the example): -#+begin_src bash +#+begin_src console $ jorge init myblog -site name: My Blog -site url: https://myblog.example.com -author: Jorge Blog - +> site name: My Blog +> site url: https://myblog.example.com +> author: Jorge Blog added myblog/config.yml added myblog/README.md added myblog/.gitignore diff --git a/docs/src/tutorial/jorge-post.org b/docs/src/tutorial/jorge-post.org index e72d520..067dc9f 100755 --- a/docs/src/tutorial/jorge-post.org +++ b/docs/src/tutorial/jorge-post.org @@ -30,14 +30,14 @@ Each website has its own layout so it's hard to predict what you may need to do From the root directory of your project run ~jorge post "My own blog post"~: -#+begin_src +#+begin_src console $ jorge post "My own blog post" added src/blog/my-own-blog-post.org #+end_src If you open the new file with your editor, you should see something like this: -#+begin_src +#+begin_src org --- title: My own blog post date: 2024-02-23 11:45:30 @@ -65,18 +65,18 @@ With ~jorge serve~ running, you can start filling in some content on this new po ** Customizing the post format As you may have noticed, the ~jorge post~ command makes a lot of assumptions: where to put it, how to name it, and what format to use. You can control some of these decisions by setting the ~post_format~ configuration key. The default is: -#+begin_src +#+begin_src yaml post_format: "blog/:title.org" #+end_src Let's say that you want to put posts in the root folder, include the date in them, and use the markdown format by default. Add this key to your ~config.yml~: -#+begin_src +#+begin_src yaml post_format: ":year-:month-:day-:title.md" #+end_src The, if you add a new post: -#+begin_src +#+begin_src console $ jorge post "Another kind of post" added src/2024-02-23-another-kind-of-post.md #+end_src diff --git a/docs/src/tutorial/jorge-serve.org b/docs/src/tutorial/jorge-serve.org index 8405f71..98eaae5 100755 --- a/docs/src/tutorial/jorge-serve.org +++ b/docs/src/tutorial/jorge-serve.org @@ -11,7 +11,7 @@ index: 3 Now that you have some [[file:jorge-init][default files]] in place, let's see how the website looks. Run ~jorge serve~ on the website directory: -#+begin_src +#+begin_src console $ cd myblog $ jorge serve building site @@ -31,7 +31,7 @@ If you open your browser at http://localhost:4001 you'll see the website we just If you open ~src/index.html~ in your editor, you should see something roughly matching what you see on the browser: -#+begin_src +#+begin_src html {% raw %} --- layout: default @@ -50,11 +50,13 @@ This file is a [[https://shopify.github.io/][liquid template]] for an HTML file. 2. The rest of the file contents will be rendered according to the liquid template syntax. 3. If it's an org-mode or markdown file, its contents will be converted to their corresponding HTML in the target. +{% raw %} In the example above, the ~layout: default~ instructs jorge to embed the index.html rendered output inside the layout defined at ~layouts/default.html~. And the liquid variables expressed by ~{{ site.config.name }}~ and ~{{ site.config.author }}~ will be replaced by the values found at ~config.yml~. +{% endraw %} If you change the markup in ~src/index.html~, you should see your browser tab refresh automatically to reflect those changes. Try, for instance, changing the title in the first header: -#+begin_src +#+begin_src html

Home sweet home

#+end_src diff --git a/go.mod b/go.mod index 733dd5a..af44ff0 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/facundoolano/jorge go 1.22.0 require ( + github.com/alecthomas/chroma/v2 v2.5.0 github.com/alecthomas/kong v0.8.1 github.com/elliotchance/orderedmap/v2 v2.2.0 github.com/fsnotify/fsnotify v1.7.0 @@ -10,6 +11,7 @@ require ( github.com/osteele/liquid v1.3.2 github.com/tdewolff/minify/v2 v2.20.16 github.com/yuin/goldmark v1.7.0 + github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc golang.org/x/net v0.0.0-20201224014010-6772e930b67b golang.org/x/text v0.3.3 gopkg.in/yaml.v2 v2.4.0 @@ -17,6 +19,7 @@ require ( ) require ( + github.com/dlclark/regexp2 v1.7.0 // indirect github.com/osteele/tuesday v1.0.3 // indirect github.com/tdewolff/parse/v2 v2.7.11 // indirect golang.org/x/sys v0.16.0 // indirect diff --git a/go.sum b/go.sum index 8f929db..85f6009 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,19 @@ -github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= -github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA= +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= +github.com/alecthomas/chroma/v2 v2.5.0 h1:CQCdj1BiBV17sD4Bd32b/Bzuiq/EqoNTrnIhyQAZ+Rk= +github.com/alecthomas/chroma/v2 v2.5.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY= github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= -github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= -github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= +github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/elliotchance/orderedmap/v2 v2.2.0 h1:7/2iwO98kYT4XkOjA9mBEIwvi4KpGB4cyHeOFOnj4Vk= github.com/elliotchance/orderedmap/v2 v2.2.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -20,6 +28,8 @@ github.com/osteele/tuesday v1.0.3 h1:SrCmo6sWwSgnvs1bivmXLvD7Ko9+aJvvkmDjB5G4FTU github.com/osteele/tuesday v1.0.3/go.mod h1:pREKpE+L03UFuR+hiznj3q7j3qB1rUZ4XfKejwWFF2M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tdewolff/minify/v2 v2.20.16 h1:/C8dtRkxLTIyUlKlBz46gDiktCrE8a6+c1gTrnPFz+U= @@ -29,8 +39,11 @@ github.com/tdewolff/parse/v2 v2.7.11/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo= github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -44,5 +57,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/markup/templates.go b/markup/templates.go index f9b95db..04c7c3c 100644 --- a/markup/templates.go +++ b/markup/templates.go @@ -9,9 +9,15 @@ import ( "path/filepath" "strings" + "github.com/alecthomas/chroma/v2" + "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/lexers" + "github.com/alecthomas/chroma/v2/styles" + "github.com/niklasfasching/go-org/org" "github.com/osteele/liquid" "github.com/yuin/goldmark" + gm_highlight "github.com/yuin/goldmark-highlighting/v2" "gopkg.in/yaml.v3" ) @@ -105,7 +111,7 @@ func (templ Template) Ext() string { // Renders the liquid template with the given context as bindings. // If the template source is org or md, convert them to html after the // liquid rendering. -func (templ Template) Render(context map[string]interface{}) ([]byte, error) { +func (templ Template) Render(context map[string]interface{}, hlTheme string) ([]byte, error) { // liquid rendering content, err := templ.liquidTemplate.Render(context) if err != nil { @@ -121,6 +127,7 @@ func (templ Template) Render(context map[string]interface{}) ([]byte, error) { // make * -> h1, ** -> h2, etc htmlWriter.TopLevelHLevel = 1 + htmlWriter.HighlightCodeBlock = highlightCodeBlock(hlTheme) contentStr, err := doc.Write(htmlWriter) if err != nil { @@ -130,7 +137,12 @@ func (templ Template) Render(context map[string]interface{}) ([]byte, error) { } else if ext == ".md" { // markdown rendering var buf bytes.Buffer - if err := goldmark.Convert(content, &buf); err != nil { + md := goldmark.New(goldmark.WithExtensions( + gm_highlight.NewHighlighting( + gm_highlight.WithStyle(hlTheme), + ), + )) + if err := md.Convert(content, &buf); err != nil { return nil, err } content = buf.Bytes() @@ -138,3 +150,28 @@ func (templ Template) Render(context map[string]interface{}) ([]byte, error) { return content, nil } + +func highlightCodeBlock(hlTheme string) func(source string, lang string, inline bool, params map[string]string) string { + // from https://github.com/niklasfasching/go-org/blob/a32df1461eb34a451b1e0dab71bd9b2558ea5dc4/blorg/util.go#L58 + return func(source, lang string, inline bool, params map[string]string) string { + var w strings.Builder + l := lexers.Get(lang) + if l == nil { + l = lexers.Fallback + } + l = chroma.Coalesce(l) + it, _ := l.Tokenise(nil, source) + options := []html.Option{} + if params[":hl_lines"] != "" { + ranges := org.ParseRanges(params[":hl_lines"]) + if ranges != nil { + options = append(options, html.HighlightLines(ranges)) + } + } + _ = html.New(options...).Format(&w, styles.Get(hlTheme), it) + if inline { + return `
` + "\n" + w.String() + "\n" + `
` + } + return `
` + "\n" + w.String() + "\n" + `
` + } +} diff --git a/markup/templates_test.go b/markup/templates_test.go index ef5987d..b70d425 100644 --- a/markup/templates_test.go +++ b/markup/templates_test.go @@ -26,7 +26,7 @@ tags: ["software", "web"] assertEqual(t, templ.Metadata["tags"].([]interface{})[0], "software") assertEqual(t, templ.Metadata["tags"].([]interface{})[1], "web") - content, err := templ.Render(nil) + content, err := templ.Render(nil, "github") assertEqual(t, err, nil) assertEqual(t, string(content), "

Hello World!

") } @@ -103,7 +103,7 @@ tags: ["software", "web"] templ, err := Parse(NewEngine("https://olano.dev", "includes"), file.Name()) assertEqual(t, err, nil) ctx := map[string]interface{}{"page": templ.Metadata} - content, err := templ.Render(ctx) + content, err := templ.Render(ctx, "github") assertEqual(t, err, nil) expected := `

my new post

a blog post

@@ -133,7 +133,7 @@ tags: ["software", "web"] templ, err := Parse(NewEngine("https://olano.dev", "includes"), file.Name()) assertEqual(t, err, nil) - content, err := templ.Render(nil) + content, err := templ.Render(nil, "github") assertEqual(t, err, nil) expected := `

@@ -175,7 +175,7 @@ tags: ["software", "web"] templ, err := Parse(NewEngine("https://olano.dev", "includes"), file.Name()) assertEqual(t, err, nil) - content, err := templ.Render(nil) + content, err := templ.Render(nil, "github") assertEqual(t, err, nil) expected := `

My title

my Subtitle

diff --git a/site/site.go b/site/site.go index abd9d32..589a040 100644 --- a/site/site.go +++ b/site/site.go @@ -295,7 +295,7 @@ func (site *Site) render(templ *markup.Template) ([]byte, error) { } ctx["page"] = templ.Metadata - content, err := templ.Render(ctx) + content, err := templ.Render(ctx, site.Config.HighlightTheme) if err != nil { return nil, err } @@ -306,7 +306,7 @@ func (site *Site) render(templ *markup.Template) ([]byte, error) { if layout_templ, ok := site.layouts[layout.(string)]; ok { ctx["layout"] = layout_templ.Metadata ctx["content"] = content - content, err = layout_templ.Render(ctx) + content, err = layout_templ.Render(ctx, site.Config.HighlightTheme) if err != nil { return nil, err } @@ -364,7 +364,7 @@ func getExcerpt(templ *markup.Template) string { ctx := map[string]interface{}{ "page": templ.Metadata, } - content, err := templ.Render(ctx) + content, err := templ.Render(ctx, "tango") if err != nil { return "" }