- Update code block regex to support symbols/numbers in language identifiers - Adjust codespan background color to a softer shade
120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
/* The majority of this from @shikijs/cli, thanks guys.
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2021 Pine Wu
|
|
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE. */
|
|
|
|
import c from "chalk";
|
|
import type { BundledLanguage, BundledTheme, HighlighterCore } from "shiki";
|
|
|
|
// From @shikijs/vscode-textmate
|
|
enum FontStyle {
|
|
NotSet = -1,
|
|
None = 0,
|
|
Italic = 1,
|
|
Bold = 2,
|
|
Underline = 4,
|
|
Strikethrough = 8,
|
|
}
|
|
|
|
export function codeToANSI(
|
|
highlighter: HighlighterCore,
|
|
code: string,
|
|
lang: BundledLanguage,
|
|
theme: BundledTheme,
|
|
): string {
|
|
let output = "";
|
|
|
|
const lines = highlighter.codeToTokensBase(code, {
|
|
lang,
|
|
theme,
|
|
});
|
|
const themeReg = highlighter.getTheme(theme);
|
|
|
|
for (const line of lines) {
|
|
for (const token of line) {
|
|
let text = token.content;
|
|
const color = token.color || themeReg.fg;
|
|
if (color) text = c.hex(hexApplyAlpha(color, themeReg.type))(text);
|
|
if (token.fontStyle) {
|
|
if (token.fontStyle & FontStyle.Bold) text = c.bold(text);
|
|
if (token.fontStyle & FontStyle.Italic) text = c.italic(text);
|
|
if (token.fontStyle & FontStyle.Underline) text = c.underline(text);
|
|
if (token.fontStyle & FontStyle.Strikethrough)
|
|
text = c.strikethrough(text);
|
|
}
|
|
output += text;
|
|
}
|
|
output += "\n";
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
const RE_HASH = /#/;
|
|
|
|
function normalizeHex(hex: string): string {
|
|
hex = hex.replace(RE_HASH, "");
|
|
if (hex.length === 3 && hex[0])
|
|
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
if (hex.length === 4 && hex[0])
|
|
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
|
|
if (hex.length === 6) hex = `${hex}ff`;
|
|
return hex.toLowerCase();
|
|
}
|
|
|
|
function hexToRgba(hex: string): {
|
|
r: number;
|
|
g: number;
|
|
b: number;
|
|
a: number;
|
|
} {
|
|
hex = normalizeHex(hex);
|
|
const r = Number.parseInt(hex.slice(0, 2), 16);
|
|
const g = Number.parseInt(hex.slice(2, 4), 16);
|
|
const b = Number.parseInt(hex.slice(4, 6), 16);
|
|
const a = Number.parseInt(hex.slice(6, 8), 16) / 255;
|
|
return { r, g, b, a };
|
|
}
|
|
|
|
function RgbToHex(r: number, g: number, b: number): string {
|
|
return [r, g, b]
|
|
.map((x) => {
|
|
const hex = x.toString(16);
|
|
return hex.length === 1 ? `0${hex}` : hex;
|
|
})
|
|
.join("");
|
|
}
|
|
|
|
export function hexApplyAlpha(
|
|
hex: string,
|
|
type: "dark" | "light" = "dark",
|
|
): string {
|
|
const { r, g, b, a } = hexToRgba(hex);
|
|
if (type === "dark") return RgbToHex(r * a, g * a, b * a);
|
|
else
|
|
return RgbToHex(
|
|
r * a + 255 * (1 - a),
|
|
g * a + 255 * (1 - a),
|
|
b * a + 255 * (1 - a),
|
|
);
|
|
}
|