Changes
5 changed files (+288/-148)
-
-
@@ -0,0 +1,25 @@## C ```c char foo; // This is line comment int main(int argc, char *argv[]) { /* This is block comment */ } ``` ## Zig ```zig export fn foo(bar: []const u8) usize { // This is line comment } /// This is doc comment pub fn main() !void { const _ = foo("Bar"); } ```
-
-
-
@@ -0,0 +1,258 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 /** @jsx h */ import { visit } from "../../../deps/esm.sh/unist-util-visit/mod.ts"; import type * as Mdast from "../../../deps/esm.sh/mdast/types.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import { refractor } from "../../../deps/esm.sh/refractor/mod.ts"; import { h } from "../../../deps/esm.sh/hastscript/mod.ts"; import { type Handlers } from "../../../deps/esm.sh/mdast-util-to-hast/mod.ts"; import { css } from "../css.ts"; const enum C { BlockContainer = "fm-code--bc", InlineCode = "fm-code--i", TokenKeyword = "fm-code--tk", TokenBuiltin = "fm-code--tb", TokenClassName = "fm-code--tc", TokenFunction = "fm-code--tf", TokenBoolean = "fm-code--tl", TokenNumber = "fm-code--tn", TokenString = "fm-code--ts", TokenChar = "fm-code--th", TokenSymbol = "fm-code--ty", TokenRegex = "fm-code--tr", TokenUrl = "fm-code--tu", TokenOperator = "fm-code--to", TokenVariable = "fm-code--tv", TokenConstant = "fm-code--ta", TokenProperty = "fm-code--tp", TokenPunctuation = "fm-code--tpu", TokenImportant = "fm-code--ti", TokenComment = "fm-code--tcm", TokenTag = "fm-code--tt", TokenAttrName = "fm-code--tan", TokenAttrValue = "fm-code--tav", TokenNamespace = "fm-code--tns", TokenProlog = "fm-code--tg", TokenDoctype = "fm-code--td", TokenCdata = "fm-code--tcd", TokenEntity = "fm-code--te", TokenBold = "fm-code--tbl", TokenItalic = "fm-code--til", TokenAtrule = "fm-code--tat", TokenSelector = "fm-code--tsl", TokenInserted = "fm-code--tin", TokenDeleted = "fm-code--tdl", } // https://prismjs.com/tokens.html const prismTokenToClassMap = new Map<string, string>([ ["keyword", C.TokenKeyword], ["builtin", C.TokenBuiltin], ["class-name", C.TokenClassName], ["function", C.TokenFunction], ["boolean", C.TokenBoolean], ["number", C.TokenNumber], ["string", C.TokenString], ["char", C.TokenChar], ["symbol", C.TokenSymbol], ["regex", C.TokenRegex], ["url", C.TokenUrl], ["operator", C.TokenOperator], ["variable", C.TokenVariable], ["constant", C.TokenConstant], ["property", C.TokenProperty], ["punctuation", C.TokenPunctuation], ["important", C.TokenImportant], ["comment", C.TokenComment], ["tag", C.TokenTag], ["attr-name", C.TokenAttrName], ["attr-value", C.TokenAttrValue], ["namespace", C.TokenNamespace], ["prolog", C.TokenProlog], ["doctype", C.TokenDoctype], ["cdata", C.TokenCdata], ["entity", C.TokenEntity], ["bold", C.TokenBold], ["italic", C.TokenItalic], ["atrule", C.TokenAtrule], ["selector", C.TokenSelector], ["inserted", C.TokenInserted], ["deleted", C.TokenDeleted], ]); // This highlighting style is inspired by https://github.com/Alligator/accent.vim // Code block intentionally breaks vertical rhythm: while code block contents is // textual, they are normally interrupts main contents' flow by default and needs // to stand out. In addition to that, reading code written in vertical rhythm is // simply a pain... // TODO: Make tabsize configurable (build config and runtime option if possible) // TODO: Make font-family configurable // TODO: Make accent color configurable export const codeStyles = css` .${C.BlockContainer} { --_accent: var(--color-primary); tab-size: 4ch; margin: 0; margin-top: calc(var(--baseline) * 1rem); padding: calc(var(--baseline) * 0.5rem) 1em; line-height: 1.5; max-width: 100%; font-size: 1rem; font-family: monospace; font-size: 0.9rem; background-color: var(--color-bg-light); color: var(--color-fg); border-radius: 4px; overflow-x: auto; } .${C.InlineCode} { margin: 0 0.2em; padding: calc(1rem / 4); font-family: monospace; font-size: 0.8rem; background-color: var(--color-bg-light); color: var(--color-fg-sub); border-radius: 4px; } .${C.TokenComment} { opacity: 0.65; } .${C.TokenString}, .${C.TokenRegex}, .${C.TokenAttrValue}, .${C.TokenNumber} { color: var(--_accent); } .${C.TokenOperator}, .${C.TokenVariable}, .${C.TokenConstant} { color: var(--color-fg); } .${C.TokenFunction}, .${C.TokenPunctuation}, .${C.TokenClassName} { color: var(--color-fg-sub); opacity: 0.9; } .${C.TokenKeyword} { color: var(--color-fg-sub); font-weight: bold; } `; function isValidClassName(value: unknown): value is string | readonly string[] { if (typeof value === "string") { return true; } if (Array.isArray(value) && value.every((x) => typeof x === "string")) { return true; } return false; } export interface CodeHandlersOptions { /** * Class name to add to the container element. * `null` to not setting class. * This is for user writing their own styles. Macana sets its own class * to the generated `<pre>` element. * * @default null */ className?: string | null; /** * Attribute name to set space separated list of node type (e.g. "token", "string", "comment"). * `null` to not set node types to attribute. * * @default null */ nodeTypeAttribute?: string | null; /** * Attribute name to set language name (e.g. "css", "html") * `null` to not set language name to attribute. * * @default null */ langNameAttribute?: string | null; } export function codeHandlers({ className = null, nodeTypeAttribute = null, langNameAttribute = null, }: CodeHandlersOptions = {}): Handlers { return { code(_state, node: Mdast.Code) { if (!node.lang || !refractor.registered(node.lang)) { return h("pre", { class: C.BlockContainer }, [ <code>{node.value}</code>, ]); } const code = refractor.highlight(node.value, node.lang); visit(code, (node) => node.type === "element", (node) => { if (node.type !== "element") { return; } if (!node.properties || !isValidClassName(node.properties.className)) { return; } const className = node.properties.className; if (!className) { return; } const classNames = Array.isArray(className) ? className : className.split(" "); let replacedClassName: string | undefined; if (classNames.includes("token")) { replacedClassName = classNames.filter((c) => c !== "token").map( (c) => { return prismTokenToClassMap.get(c); }, ).filter((c) => !!c).join(" "); } node.properties.className = replacedClassName; if (typeof nodeTypeAttribute === "string") { node.properties[nodeTypeAttribute] = className; } }); return h("pre", { class: [C.BlockContainer, className].filter((s): s is string => !!s) .join(" "), ...(langNameAttribute ? { [langNameAttribute]: node.lang } : {}), }, [ <code>{code.children as Hast.ElementContent[]}</code>, ]); }, inlineCode(_state, node: Mdast.InlineCode) { return h("code", { class: C.InlineCode }, [node.value]); }, }; }
-
-
-
@@ -12,13 +12,12 @@ ofmHtml,ofmToHastHandlers, } from "../../../content_parser/obsidian_markdown.ts"; import { syntaxHighlightingHandlers } from "../mdast/syntax_highlighting_handlers.ts"; import { css, join as joinCss } from "../css.ts"; import { calloutHandlers, calloutStyles } from "./callout.tsx"; import { listHandlers, listStyles } from "./list.tsx"; import { mathHandlers } from "./math.ts"; import { codeHandlers, codeStyles } from "./code.tsx"; const enum C { Wrapper = "fm--m",
-
@@ -39,38 +38,6 @@:where(.${C.Wrapper}) p { margin: 0; margin-top: calc(var(--baseline) * 1rem); } :where(.${C.Wrapper}) pre { margin: 0; margin-top: calc(var(--baseline) * 1rem) !important; padding: calc(var(--baseline) * 1rem) 1em !important; line-height: calc(var(--baseline) * 1rem); max-width: 100%; font-size: 1rem; background-color: var(--color-fg); color: var(--color-bg); border-radius: calc(1rem / 4); overflow-x: auto; } :where(.${C.Wrapper}) pre > code { all: unset; } :where(.${C.Wrapper}) code { margin: 0 0.2em; padding: calc(1rem / 4); background-color: var(--color-bg-accent); color: var(--color-fg-sub); border-radius: calc(1rem / 4); font-family: "Ubuntu Mono", monospace; } :where(.${C.Wrapper}) pre > code .token.comment { font-style: italic; } :where(.${C.Wrapper}) a,
-
@@ -203,6 +170,7 @@ export const fromMdastStyles = joinCss(ownStyles, calloutStyles, listStyles, codeStyles, ); export function fromMdast(mdast: Mdast.Nodes): Hast.Nodes {
-
@@ -211,8 +179,8 @@ handlers: {...ofmToHastHandlers(), ...calloutHandlers(), ...listHandlers(), ...syntaxHighlightingHandlers(), ...mathHandlers(), ...codeHandlers(), }, allowDangerousHtml: true, }));
-
-
-
@@ -16,6 +16,7 @@ --color-primary: rgb(217, 59, 133);--color-bg: #fafafa; --color-bg-accent: #eaeaea; --color-bg-light: #f4f4f3; --color-fg: #333; --color-fg-sub: #534c37; --color-fg-light: #c5c5c5;
-
@@ -61,6 +62,7 @@ @media (prefers-color-scheme: dark) {:root { --color-bg: #222228; --color-bg-accent: #323135; --color-bg-light: #2b2b2d; --color-fg: #fafafa; --color-fg-sub: #f3edd9; --color-fg-light: #c5c5c5;
-
@@ -116,19 +118,5 @@ text-decoration: underline;} a[data-hash-link]:focus { opacity: 1; } /* Syntax highlight */ .macana--highlight [data-hl-node~="token"][data-hl-node~="content"] { color: var(--color-bg); } .macana--highlight [data-hl-node~="token"][data-hl-node~="punctuation"] { color: #ccc; } @media (prefers-color-scheme: dark) { .macana--highlight [data-hl-node~="token"][data-hl-node~="punctuation"] { color: #888; } } `;
-
-
-
@@ -1,99 +0,0 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // // SPDX-License-Identifier: Apache-2.0 import { visit } from "../../../deps/esm.sh/unist-util-visit/mod.ts"; import { refractor } from "../../../deps/esm.sh/refractor/mod.ts"; import type * as Mdast from "../../../deps/esm.sh/mdast/types.ts"; import type * as Hast from "../../../deps/esm.sh/hast/types.ts"; import { defaultHandlers, type Handlers, type State, } from "../../../deps/esm.sh/mdast-util-to-hast/mod.ts"; function isValidClassName(value: unknown): value is string | readonly string[] { if (typeof value === "string") { return true; } if (Array.isArray(value) && value.every((x) => typeof x === "string")) { return true; } return false; } interface SyntaxHighlightingOptions { /** * Class name to add to the container element. * `null` to not setting class. * * @default "macana--highlight" */ className?: string | null; /** * Attribute name to set space separated list of node type (e.g. "token", "string", "comment"). * `null` to not set node types to attribute. * * @default "data-hl-node" */ nodeTypeAttribute?: string | null; /** * Attribute name to set language name (e.g. "css", "html") * `null` to not set language name to attribute. * * @default "data-hl-lang" */ langNameAttribute?: string | null; } export function syntaxHighlightingHandlers({ className = "macana--highlight", nodeTypeAttribute = "data-hl-node", langNameAttribute = "data-hl-lang", }: SyntaxHighlightingOptions = {}): Handlers { return { code(state: State, node: Mdast.Code) { if (!node.lang || !refractor.registered(node.lang)) { return defaultHandlers.code(state, node); } const code = refractor.highlight(node.value, node.lang); visit(code, (node) => node.type === "element", (node) => { if (node.type !== "element") { return; } if (!node.properties || !isValidClassName(node.properties.className)) { return; } const className = node.properties.className; node.properties.className = undefined; if (typeof nodeTypeAttribute === "string") { node.properties[nodeTypeAttribute] = className; } }); return { type: "element", tagName: "pre", properties: { className, ...(langNameAttribute ? { [langNameAttribute]: node.lang } : {}), }, children: [ { type: "element", tagName: "code", properties: {}, children: code.children as Hast.ElementContent[], }, ], }; }, }; }
-