痞客邦部落格程式上色相關文章:
- Part1: SyntaxHighLighter
- Part2: highlight.js
- Part3: Prim.js
Prism.js
最近 (2024/09) 無意間看到介紹 Prism.js 的文章, 現下來試試, 看看是否比 highlight.js 好用.
個人大致看完 prism.js 網站上的說明, 除了基本的上色功能之外, 還有一個自動載入程式 (auto loader) 及許許多多的 plugin (叫我看得眼花撩亂). 總之, 不是我習慣的風格. 不過沒關係, 反正就試用一下應該不成問題.
基本上色功能
- 和 hljs 一樣, 它的上色主要對象是用 <pre>+<code> 這二個 tag 括住的程式片斷. 還有, 夾雜在一般文字段內的 <code> 片斷, 只要指定其上色的語言, 它也可以為之上色.
- 然後要在 <code> 上使用 class="language-xxxx" 或者 class="lang-xxxx" 指定要上色的語言.
- 要上色的第一行程式必需立即接在 <code> tag 後面.
- 程式的最後一行後面也最好立即接上 </code>
<pre><code class="language-CPP">從這裡開始寫你要上色的程式碼
...
你要上色的程式碼
...</code>
</pre>
和其他上色套件一樣, 使用前先載人它的核心套件及上色主題 (theme). 不過, 它的官方上色主題只有少數幾種 (不似 hljs 那樣種類繁多, 令人眼花撩亂). 另外, github 上的 "Prism Themes" 這個 repository 上面收集了不少用戶提供的上色主題, 它一樣可以由各大 cdn 下載.
<!-- Prism 上色主題的載入路徑 -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" />
<!-- Prism-themes 上色主題的載入路徑 -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism-themes/1.9.0/prism-a11y-dark.min.css" rel="stylesheet" />
<!-- 載入上色主題 -->
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
<!-- 載入 Prism.js -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<!-- 接著載入其他語言模組 -->
除了上述的標準寫法, prism.js 官方有提供自動載入程式, 可以協助我們載入整個頁面中使用到的其他語言 (即: 不必自己載入需要的上色語言模組). 使用時, 我們只需載入 prism.js 的核心元件: components/prism-core.min.js 及自動載入插件: plugins/autoloader/prism-autoloader.min.js, 其他需用到的語言模組不必自行載入.
<!-- 載入上色主題 -->
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
<!-- 只載入 Prism.js 核心元件 -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<!-- 載入 autoloader -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
不過個入發現 autoloader 對 html+css+js 三者混合的程式片斷判定不是很精準, 所以我們可以把核心元件部份改回 prism.js, 然後一樣用 autoloader 為我們載入其他的語言模組, 如下:
<!-- 載入上色主題 -->
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
<!-- 載入 Prism.js -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<!-- 載入 autoloader -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
附加行號
和 hljs 一樣, 附加行號功能是由插件提供的.
<!-- 載入插件 '附加行號' 的 CSS -->
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet" />
<!-- 載入插件 '附加行號' -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
- 然後為需要附加行號的 <pre> tag 加上 class='line-numbers'.
- 也可以在一個範圍比較大的區塊上附加 class='line-numbers', 這樣可以省去為區塊內的每一個 <pre> tag 加上 class='line-numbers'.
- 沒有現成的大區塊, 則可以在需要的範圍上外加一個
<div class='line-numbers'>
tag 包住它們. - 區塊內如果有不想附上行號的 <pre> tag, 則加上 class='no-line-numbers'.
- 沒有現成的大區塊, 則可以在需要的範圍上外加一個
- 指定行號的起始值需要在 <pre> tag 上附加上 data-start='起始值' 的屬性. 例如, 行號由 7 開始:
<pre data-start='7'>
附加拷貝按鈕
拷貝按鈕功能也是由插件提供, 不過它需要另一個叫 toolbar 的插件.
<!-- 載入插件 'toolbar' 的 CSS -->
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet" />
<!-- 載入插件 'toolbar' -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<!-- 載入插件 '拷貝按鈕' -->
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
拷貝按鈕可以用 HTML tag 的屬性進行設定. 總共有三個狀態訊息及一個計時器設定, 我們可以在 <pre> tag 對它進行訊息本地化及設定調整. 或者也可以像附加行號的插件一樣, 將設定搬到一個範圍比較大的上層 <div> 區塊.
- data-prismjs-copy: 預設訊息, 尚未拷貝狀態.
正體中文本地化應將此訊息設為 "複製" 或者 "拷貝" 等類似詞語. - data-prismjs-copy-error: 拷貝錯誤訊息.
正體中文本地化應將此訊息設為 "按 Ctrl+C, 複製" 或類似詞語. - data-prismjs-copy-success: 拷貝成功訊息.
正體中文本地化應將此訊息設為 "已複製" 或類似詞語. - data-prismjs-copy-timeout: 拷貝成功/錯誤訊息的持續時間 (單位為 ms). 預設值為 5,000ms.
客製化按鈕外觀指南 (Styling)
我們可以為拷貝按鈕的按鈕元件進行 CSS 客製化, 該按鈕元件的 HTML 如下:
<button class="copy-to-clipboard-button" type="button" data-copy-state="copy">
<span>Copy</span>
</button>
使用 .copy-to-clipboard-button 可以定位到拷貝按鈕. 另外屬性 data-copy-state 有三個狀態:
- data-copy-state="copy": 預設訊息, 尚未拷貝狀態.
- data-copy-state="copy-error": 顯示拷貝錯誤訊息時.
- data-copy-state="copy-success": 顯示拷貝成功訊息時.
使用 .copy-to-clipboard-button:not([data-copy-state="copy"]) 可以定位到非預設訊息的另外二個狀態.
小結
總結以上說明, 在部落格文章的後面貼上以下 HTML:
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
就可以使用 Prism.js 進行程式上色了.
Prism.js 客製化 CSS
在我自己的部落格中, Prism.js 免不了也是要客製化一下, 調整一下外觀. 由於和 pixnet 部落格衝突比較多, 所以我直接取消載入二個 plugin 的 CSS, 並直接將其內容抄到我的自訂 CSS 檔裡.
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
在此我客製化了以下項目:
- 修正痞客邦部落格行動版的三個 CSS 定位太大造成的影響.
- 微調附加行號插件的外觀. (使用二個 CSS 變數)
- 拷貝按鈕外觀改為 SVG 圖片. (這個是由 hljs 的 copy buttton 移值過來的, 工程好大)
最後的 myPrism.css 內容如下:
/* Fix for Pixnet Mobile */
.article-content-inner .tag span,
.article-content-inner .tag {
background:revert;
padding:revert;
margin:revert;
border-radius:revert;
font-size:revert;
}
.article-content-inner .comment {
font-family: revert;
font-size:revert;
padding:revert;
border:revert;
letter-spacing: revert;
}
.article-content-inner span { white-space:unset!important }
/* Adjustment for Prism line-number */
.article-content-inner {
--ln-width:2.5em;
--ln-rPad:0.5em;
}
pre[class*=language-] {
margin:0!important;
padding:0.25em!important;
}
/* From Prism line-number with patches */
pre[class*=language-].line-numbers {
position: relative;
padding-left:calc(var(--ln-width) + var(--ln-rPad))!important;
counter-reset: linenumber
}
pre[class*=language-].line-numbers>code {
position: relative;
white-space: inherit
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: -2px;
font-size: 100%;
left: calc(0px - var(--ln-rPad) - var(--ln-width));
width: var(--ln-width)!important;
letter-spacing: -1px;
border-right: 2px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none
}
.line-numbers-rows>span {
display: block;
counter-increment: linenumber
}
.line-numbers-rows>span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: var(--ln-rPad);
text-align: right
}
/* Adjustment for Prism Copy button */
div.code-toolbar {
position:relative;
overflow:hidden;
transform: translateZ(0)
}
div.code-toolbar>.toolbar {
--theme-color: white;
--theme-background: black;
--theme-padding: 3.6px;
position:absolute;
top:0.25em;
right:0.25em;
transition:transform 200ms ease-out;
}
div.code-toolbar .toolbar-item>button {
position: relative;
margin: calc(var(--theme-padding) / 2);
width: calc(16px + var(--theme-padding));
height: calc(16px + var(--theme-padding));
font-size: .8125rem;
text-indent: -9999px;
color: var(--theme-color);
border-radius: .25rem;
border: 1px solid;
border-color: color-mix(in srgb,var(--theme-color),transparent 80%);
background-color: var(--theme-background);
transition: background-color 200ms ease;
overflow: hidden
}
div.code-toolbar .toolbar-item>button[data-copy-state="copy"]::before {
content: "";
width: 1rem;
height: 1rem;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
position: absolute;
background-color: currentColor;
mask: url('data:image/svg+xml;utf-8,');
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center center
}
.toolbar {
transform: translateX(calc(100% + 1.125em))
}
div.code-toolbar:focus-within .toolbar {
transition: none;
transform: translateX(0)
}
div.code-toolbar:hover .toolbar {
transform: translateX(0)
}
.toolbar-item>button:not([data-copy-state="copy"]) {
text-indent: 0!important;
width: auto!important;
}
試用心得
- Prism.js 及其插件都不需額外的 JS 來啟動, 只用 HTML 的 tag 屬性來調整設定, 在部落格中 (非自行架設的) 使用上相對容易. highlight.js 比較麻煩一些, 不只 hljs 本身需要額外寫一行啟動用的 JS, 插件也需要用 addPlugin() 附加到 hljs 上.
- Prism.js 支援 inline 上色 (即一般文字行中的 <code> tag), 無需額外的設定. highlight.js 目前的版本 (v11.10) 也支援 inline 上色. 不過,
- 需要在上色前使用 hljs.configure({cssSelector: 'pre code, span code'}) 調整設定.
- 此為個人測試時使用的, 非官方建議設定值.
- 如果有使用附加行號的 highlightjs-line-numbers.js 插件並設定 {singleLine: true}, 則要多附加一個 class: nohljsln.
- 因此, 個人建議直接關閉此設定值 (即不加, 或者設為 false).
- 附加拷貝按鈕的 highlightjs-copy 插件: 暫時沒有關閉功能. 它一定會干擾到正常 hljs 的工作.
- 需要在上色前使用 hljs.configure({cssSelector: 'pre code, span code'}) 調整設定.
- Prism.js 對 CSS Selector 的解析比較粗略, highlight.js 在此一部份比較精細.