在Hugo中利用Prism进行代码高亮
文章目录
简要介绍如何在基于Even主题的Hugo博客中采用Prism替换默认的Chroma进行代码高亮显示,实现支持更多的语言和更好的识别。
背景
个人博客采用Hugo编写已经有年头了,由于Hugo是基于Golang编写的,而Chroma也是基于Golang编写的,故Hugo采用了Chroma作为其默认的代码高亮显示实现1。自己的个人博客搭建完毕之后一直采用该默认实现。
之后的几年虽然自己的博客主题切换成了Even主题,也伴随着各种优化,但对于代码高亮这块却一直没变化,一方面是自己觉得其显示效果还行,没找到更适合自己口味的显示样式,就和代码开发一样,能正常运行的代码就不要随便优化重构(虽然自己很喜欢代码重构),另一方面则是由于代码高亮切换,改动时涉及的底层页面较多。
近期由于部门内部要搭建基于GitBook的知识库,在此过程中采用的语法高亮为Prism,经过一段时间的时候之后个人感觉Prism对于代码高亮显示的支持更强,对它们的关键指标进行对比后,初步决定采用Prism替换Chroma。
| Prime | Chroma | |
|---|---|---|
| GitHub地址 | https://github.com/PrismJS/prism | https://github.com/alecthomas/chroma |
| Star数 | 12.2k | 4.3k |
| 支持语言数 | 297 | 249 |
| 插件支持 | 是 | 否 |
| 渲染性能 | 中 | 高 |
| 模块化 | 是 | 否 |
在此过程中自己也参考了其它的一些高亮方案,尤其是这篇文章通过表格形式详细的对比常用的代码高亮框架,虽然有比Prism更强的实现,但Prism已经基本上满足自己的要求,同时自己在GitBook的搭建过程中也积累了一定的使用经验,故最终决定采用Prism替换Chroma。
操作步骤
由于修改时涉及到的工作量较大,考虑到自己的Golang和Hugo是半吊子水平,我决定基于已有的进行改进。
简单搜索一番后,在这条回复下找到了Hugo核心开发人员Joe Mooring给出了其已经开发好的demo,基于下述指令可直接运行测试
git clone --single-branch -b hugo-forum-topic-38309 https://github.com/jmooring/hugo-testing hugo-forum-topic-38309
cd hugo-forum-topic-38309
hugo server
本地运行后发现确实能够正常使用,但与自己在GitBook中看到的效果有一定的差距,决定对其进行分步骤改造,相关操作步骤如下:
-
在
layouts/_default/_markup下新建文件render-codeblock.html,其内容如下,其内容相对于hugo-forum-topic-38309分支上的原始文件做了一定的改动,主要是添加自动显示行号相关的信息<!-- only show line numbers when there is more than one line or specific it manually --> {{- $classStr := "language-%s" }} {{- $lineCount := len (split .Inner "\n") -}} {{- if gt $lineCount 1 }} {{- $classStr = print "line-numbers " print $classStr }} {{- else -}} {{- $classStr = print "no-line-numbers " print $classStr }} {{- end }} {{- $attributes := .Attributes }} {{- $classes := slice (printf $classStr .Type) .Attributes.class }} {{- $attributes = merge $attributes (dict "class" (delimit $classes " ")) }} <pre {{- range $k, $v := $attributes }} {{- printf " %s=%q" $k $v | safeHTMLAttr }} {{- end -}} > <code> {{- .Inner -}} </code> </pre> {{- .Page.Store.Set "hasCodeBlock" true }} -
在
assets/sass/_custom/_custom.scss中添加如下内容,主要用于正确显示行号位置.line-numbers .line-numbers-rows{ border-right: 0px !important; } /*只有一行代码时没必要显示行号*/ .no-line-numbers{ position: relative } -
将
assets/sass/_partial/_post/_code.scss的内容精简保留如下,其余的需要移除掉,否则由于样式冲突导致显示不正常code, pre { /* padding: 7px; font-size: $code-font-size;*/ font-family: $code-font-family; background: $code-background; } code { /*padding: 3px 5px;*/ border-radius: 4px; color: $code-color; } pre[class*=language-] { font-size: $code-font-size; } -
将
config.toml中的下述内容移除掉[markup.highlight] anchorLineNos = false codeFences = true guessSyntax = true hl_Lines = "" lineAnchors = "" lineNoStart =1 lineNos = true lineNumbersInTable = true noClasses = true style = "monokai" tabWidth = 4 -
在
layouts/partials/head.html中添加如下内容{{- if .Store.Get "hasCodeBlock" -}} <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-solarizedlight.min.css"/> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-highlight/prism-line-highlight.min.css"/> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-numbers/prism-line-numbers.min.css"/> {{- end -}} -
在
layouts/partials/scripts.html中添加如下内容{{ if .Store.Get "hasCodeBlock" }} <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-highlight/prism-line-highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script> {{ end }}同时代码复制部分的源码修改如下,以便保留原有的代码复制按钮样式
<!-- copy to clipboard --> {{- if .Site.Params.enableCopyCode -}} <script> let copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"/> </svg>`; let copiedIcon=`<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="m2.25 12.321 7.27 6.491c.143.127.321.19.499.19.206 0 .41-.084.559-.249l11.23-12.501c.129-.143.192-.321.192-.5 0-.419-.338-.75-.749-.75-.206 0-.411.084-.559.249l-10.731 11.945-6.711-5.994c-.144-.127-.322-.19-.5-.19-.417 0-.75.336-.75.749 0 .206.084.412.25.56" fill-rule="nonzero"/></svg>`; function createCopyButton(codeDiv) { const div = document.createElement("div"); div.className = "copy-code"; div.innerHTML = copyIcon; div.addEventListener("click", () => copyCodeToClipboard(div, codeDiv) ); addCopyButtonToDom(div, codeDiv); } async function copyCodeToClipboard(button, codeDiv) { const codeToCopy = codeDiv.querySelector(":scope > code") .innerText; await navigator.clipboard.writeText(codeToCopy); button.blur(); button.innerHTML = copiedIcon; setTimeout(() => button.innerHTML = copyIcon, 2000); } function addCopyButtonToDom(button, codeDiv) { const wrapper = document.createElement("div"); wrapper.className = "highlight-wrapper"; codeDiv.parentNode.insertBefore(wrapper, codeDiv); wrapper.appendChild(codeDiv); wrapper.insertBefore(button, wrapper.firstChild); } var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); if(!isMobile){ document.querySelectorAll("pre[class*=language]").forEach((codeDiv) => createCopyButton(codeDiv)); } </script> {{ end }}
效果对比
基于前述步骤修改完毕后,可进行简单的测试,测试对比效果如下:
-
Java代码显示结果对比
-
JavaScript代码显示结果对比
可看出整体上
Prismjs的显示效果与识别度整体上比Chroma要更好。
注意事项
-
由于
Prismjs在数据库部分支持的语言为sql,对于细分的mysql、sql server等尚未提供支持,所以需要将涉及到的代码块语言进行统一修改
-
测试
Prismjs非Hugo原生的,故在引入对应的js和css文件时,需要采用适合自己网络环境的CDN或本地文件,本例中采用的是基于https://cdn.jsdelivr.net的网络文件