feat: support custom theme.

feat: support to set default theme color and css variable prefix options.
This commit is contained in:
HPCesia 2024-10-21 16:40:34 +08:00
parent b3068350db
commit c03510a8a7
3 changed files with 47 additions and 14 deletions

View File

@ -1,4 +1,6 @@
# Hexo-Highlight-Shiki # Hexo-Highlight-Shiki
![NPM Version](https://img.shields.io/npm/v/hexo-highlighter-shiki?style=flat)
English丨[简体中文](README_zh-CN.md) English丨[简体中文](README_zh-CN.md)
A hexo plugin to use [Shiki](https://github.com/shikijs/shiki) as code block highlighter. A hexo plugin to use [Shiki](https://github.com/shikijs/shiki) as code block highlighter.
@ -33,11 +35,15 @@ The complete configuration is as follows:
```yaml ```yaml
shiki: shiki:
theme: one-dark-pro # see https://shiki.style/themes for supported themes. theme: one-dark-pro # see https://shiki.style/themes for supported themes.
line_number: false line_number: false # default: false
strip_indent: true strip_indent: true # default: true
tab_replace: " " tab_replace: " " # default: " "
pre_style: true # Preserve the style of the <pre> tag, i.e., the theme's `background-color`. pre_style: true # Preserve the style of the <pre> tag, i.e., the theme's `background-color`. default: true
default_color: light # Only take effect when using multiple themes. default: light
css_variable_prefix: --shiki- # Only take effect when using multiple themes. default: --shiki-
additional: additional:
themes: # List of the TextMate theme json to be added.
- path/to/theme.json
langs: # List of the TextMate grammar json of languages to be added. langs: # List of the TextMate grammar json of languages to be added.
- path/to/lang_grammar.json - path/to/lang_grammar.json
lang_alias: # List of the alias of languages. lang_alias: # List of the alias of languages.

View File

@ -1,4 +1,6 @@
# Hexo-Highlight-Shiki # Hexo-Highlight-Shiki
![NPM Version](https://img.shields.io/npm/v/hexo-highlighter-shiki?style=flat)
[English](README.md)丨简体中文 [English](README.md)丨简体中文
一个使用 [Shiki](https://github.com/shikijs/shiki) 作为代码块高亮器的 Hexo 插件。 一个使用 [Shiki](https://github.com/shikijs/shiki) 作为代码块高亮器的 Hexo 插件。
@ -31,15 +33,18 @@ shiki:
完整配置如下: 完整配置如下:
```yaml ```yaml
shiki: shiki:
theme: "one-dark-pro" # Theme, see https://shiki.style/themes for supported themes. theme: "one-dark-pro" # 主题,请参阅 https://shiki.style/themes 以获取支持的主题列表。
line_number: false line_number: false
strip_indent: true strip_indent: true
tab_replace: " " tab_replace: " "
pre_style: true # Preserve the style of the <pre> tag, i.e., the theme's `background-color`. pre_style: true # 保留 <pre> 标签的样式,即主题的 `background-color`
default_color: light # 仅在同时使用多个主题时生效。默认值light
css_variable_prefix: --shiki- # 仅在同时使用多个主题时生效。默认值:--shiki-
additional: additional:
langs: # List of the TextMate grammar json of languages to be added. themes: # 要添加的主题的 TextMate 主题 json 列表。
langs: # 要添加的语言的 TextMate 语法 json 列表。
- path/to/lang_grammar.json - path/to/lang_grammar.json
lang_alias: # List of the alias of languages. lang_alias: # 语言的别名列表。
your_alias1: lang_name1 your_alias1: lang_name1
your_alias2: lang_name2 your_alias2: lang_name2
``` ```

View File

@ -1,5 +1,5 @@
"use strict"; "use strict";
import { bundledLanguages, createHighlighter, ShikiTransformer } from "shiki"; import { bundledLanguages, bundledThemes, createHighlighter, ShikiTransformer } from "shiki";
import type Hexo from "hexo"; import type Hexo from "hexo";
import { HighlightOptions } from "hexo/dist/extend/syntax_highlight"; import { HighlightOptions } from "hexo/dist/extend/syntax_highlight";
import { stripIndent, htmlTag } from "hexo-util"; import { stripIndent, htmlTag } from "hexo-util";
@ -13,22 +13,41 @@ export async function init(hexo: Hexo) {
strip_indent: true, strip_indent: true,
tab_replace: " ", tab_replace: " ",
pre_style: true, pre_style: true,
default_color: "light",
css_variable_prefix: "--shiki-",
additional: { additional: {
themes: [],
langs: [], langs: [],
lang_alias: {}, lang_alias: {},
}, },
}, },
hexo.config.shiki || {} hexo.config.shiki || {}
); );
// 处理自定义语言 json
let additional_langs = []; let additional_langs = [];
if (config.additional.langs) if (config.additional.langs)
for (const extra_lang of [].concat(config.additional.langs)) { for (const extra_lang of [].concat(config.additional.langs)) {
additional_langs.push(JSON.parse(readFileSync(extra_lang, "utf8"))); additional_langs.push(JSON.parse(readFileSync(extra_lang, "utf8")));
} }
const langs = [...Object.keys(bundledLanguages), ...additional_langs]; const langs = [...Object.keys(bundledLanguages), ...additional_langs];
// 处理自定义主题 json
let additional_themes = [];
if (config.additional.themes)
for (const extra_theme of [].concat(config.additional.themes)) {
additional_themes.push(JSON.parse(readFileSync(extra_theme, "utf8")));
}
const themes = additional_themes.concat(
(typeof config.theme === "string" ? [config.theme] : Object.values(config.theme)).filter(
(theme) => theme in bundledThemes
)
);
// 创建 highlighter
let highlighter_options = { let highlighter_options = {
langs: langs, langs: langs,
themes: typeof config.theme === "string" ? [config.theme] : Object.values(config.theme), themes: themes,
}; };
if (config.additional.lang_alias && Object.keys(config.additional.lang_alias).length > 0) { if (config.additional.lang_alias && Object.keys(config.additional.lang_alias).length > 0) {
highlighter_options["langAliases"] = config.additional.lang_alias; highlighter_options["langAliases"] = config.additional.lang_alias;
@ -45,6 +64,7 @@ export async function init(hexo: Hexo) {
ansi: true, ansi: true,
} }
); );
const hexoHighlighter = (code: string, options: HighlightOptions) => { const hexoHighlighter = (code: string, options: HighlightOptions) => {
var code = config.strip_indent ? (stripIndent(code) as string) : code; var code = config.strip_indent ? (stripIndent(code) as string) : code;
code = config.tab_replace ? code.replace(/\t/g, config.tab_replace) : code; code = config.tab_replace ? code.replace(/\t/g, config.tab_replace) : code;
@ -57,7 +77,7 @@ export async function init(hexo: Hexo) {
// 处理代码块语法高亮 // 处理代码块语法高亮
let pre = ""; let pre = "";
const transformer = (): ShikiTransformer => { const transformerMarkedLine = (): ShikiTransformer => {
return { return {
line(node, line) { line(node, line) {
if (options.mark && options.mark.includes(line)) { if (options.mark && options.mark.includes(line)) {
@ -71,13 +91,15 @@ export async function init(hexo: Hexo) {
pre = highlighter.codeToHtml(code, { pre = highlighter.codeToHtml(code, {
lang, lang,
theme: config.theme, theme: config.theme,
transformers: [transformer()], transformers: [transformerMarkedLine()],
}); });
else else
pre = highlighter.codeToHtml(code, { pre = highlighter.codeToHtml(code, {
lang, lang,
themes: config.theme, themes: config.theme,
transformers: [transformer()], transformers: [transformerMarkedLine()],
defaultColor: config.default_color,
cssVariablePrefix: config.css_variable_prefix,
}); });
// 删除多余内容 // 删除多余内容
pre = pre.replace(/<pre[^>]*>/, (match) => { pre = pre.replace(/<pre[^>]*>/, (match) => {
@ -97,7 +119,7 @@ export async function init(hexo: Hexo) {
(options.line_number || true) && // 代码块中未设置不显示行号 (options.line_number || true) && // 代码块中未设置不显示行号
(options.line_threshold || 0) < options.lines_length; // 代码行数超过阈值 (options.line_threshold || 0) < options.lines_length; // 代码行数超过阈值
if (show_line_number) { if (show_line_number) {
const firstLine = options.firstLine || 1 const firstLine = options.firstLine || 1;
for (let i = firstLine, len = code.split("\n").length + firstLine; i < len; i++) { for (let i = firstLine, len = code.split("\n").length + firstLine; i < len; i++) {
numbers += htmlTag("span", { class: "line" }, `${i}`, false) + "<br>"; numbers += htmlTag("span", { class: "line" }, `${i}`, false) + "<br>";
} }