diff --git a/ec.config.mjs b/ec.config.mjs new file mode 100644 index 0000000..c03eb5a --- /dev/null +++ b/ec.config.mjs @@ -0,0 +1,7 @@ +// @ts-check +import { pluginCxxMark } from "./src/plugins/expressive-code/cxx-mark.ts"; + +/** @type {import('@astrojs/starlight/expressive-code').StarlightExpressiveCodeOptions} */ +export default { + plugins: [pluginCxxMark()], +}; diff --git a/src/assets/examples/development/guide/cxx-mark-1.mdx b/src/assets/examples/development/guide/cxx-mark-1.mdx new file mode 100644 index 0000000..bbc0df8 --- /dev/null +++ b/src/assets/examples/development/guide/cxx-mark-1.mdx @@ -0,0 +1,8 @@ +```cpp cxx-mark +// syntax of for statement +/*$s:attr*//*$opt*/ for ( /*$s:init-statement*/ /*$s:condition*//*$opt*/ ; /*$s:expression*//*$opt*/ ) /*$s:statement*/ + +// exposition only alias template of conditionally const type +template< bool Const, class T > +using /*$e:maybe-const*/ = std::conditional_t; +``` \ No newline at end of file diff --git a/src/content/docs/development/guide/doc-everything.mdx b/src/content/docs/development/guide/doc-everything.mdx index d6e7f8d..edf6d31 100644 --- a/src/content/docs/development/guide/doc-everything.mdx +++ b/src/content/docs/development/guide/doc-everything.mdx @@ -139,6 +139,75 @@ import declDocExample3 from "@src/assets/examples/development/guide/decl-doc-3.m Don't forget the `autorevSince` attribute on the `DeclDoc` component! Presence of this attribute allows [autorev](./revision#autorev) to automatically show and hide the `DeclDoc` component according to the user-selected revision. +## Code block inline markers + +Special comments can be embedded within Markdown code blocks to specify part of codes has special meanings. Enable this feature by adding `cxx-mark` after the language name of code block's opening fence: + +import cxxMarkExample1 from "@src/assets/examples/development/guide/cxx-mark-1.mdx?raw"; + + + + + ```cpp cxx-mark + // syntax of for statement + /*$s:attr*//*$opt*/ for ( /*$s:init-statement*/ /*$s:condition*//*$opt*/ ; /*$s:expression*//*$opt*/ ) /*$s:statement*/ + + // exposition only alias template of conditionally const type + template< bool Const, class T > + using /*$e:maybe-const*/ = std::conditional_t; + ``` + + +Three kinds of markers are supported: + + + + ``` + /*$s:something*/ + ``` + + + Marks part of code to be a syntax notation. + + Rendered as: + + ```cpp cxx-mark + /*$s:something*/ + ``` + + + + + ``` + /*$e:something*/ + ``` + + + Marks part of code to be exposition only. + + Rendered as: + + ```cpp cxx-mark + /*$e:something*/ + ``` + + + + + ``` + something/*$opt*/ + ``` + + + Mark the previous part of code (often a syntax notation) to be optional. + + Rendered as: + + ```cpp cxx-mark + something/*$opt*/ + ``` + + ## Description list ### Basic Usage diff --git a/src/plugins/expressive-code/cxx-mark.ts b/src/plugins/expressive-code/cxx-mark.ts new file mode 100644 index 0000000..5155d4f --- /dev/null +++ b/src/plugins/expressive-code/cxx-mark.ts @@ -0,0 +1,63 @@ +import { + definePlugin, + InlineStyleAnnotation, + type ExpressiveCodePlugin, +} from "@astrojs/starlight/expressive-code"; + +export function pluginCxxMark(): ExpressiveCodePlugin { + return definePlugin({ + name: "CXX mark", + hooks: { + postprocessAnalyzedCode: (context) => { + context.codeBlock.getLines().forEach((line) => { + if (context.codeBlock.meta.includes("cxx-mark")) { + const matches = [ + ...line.text.matchAll(/\/\*\$(.+?)\*\//g), + ].reverse(); + matches.forEach((match) => { + const begin = match.index; + const end = begin + match[0].length; + if (match[1].startsWith("s:")) { + line.addAnnotation( + new InlineStyleAnnotation({ + inlineRange: { + columnStart: begin, + columnEnd: end, + }, + // color of syntax notation should be same with comments + italic: true, + }) + ); + line.editText(begin, end, match[0].slice(5, -2)); + } else if (match[1].startsWith("e:")) { + line.addAnnotation( + new InlineStyleAnnotation({ + inlineRange: { + columnStart: begin, + columnEnd: end, + }, + color: "var(--cppdoc-color-cxx-mark-exposition)", + italic: true, + }) + ); + line.editText(begin + 2, begin + 5, ""); + } else if (match[1] == "opt") { + const newStr = "(optional)"; + line.editText(begin, end, newStr); + line.addAnnotation( + new InlineStyleAnnotation({ + inlineRange: { + columnStart: begin, + columnEnd: begin + newStr.length, + }, + color: "var(--cppdoc-color-cxx-mark-optional)", + }) + ); + } + }); + } + }); + }, + }, + }); +} diff --git a/src/styles/custom.css b/src/styles/custom.css index efbe066..aff9dbd 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -17,6 +17,9 @@ --cppdoc-color-ill-formed: var(--sl-color-red); --cppdoc-color-ifndr: var(--sl-color-red); + --cppdoc-color-cxx-mark-exposition: #ff0000; + --cppdoc-color-cxx-mark-optional: var(--sl-color-green); + --sl-sidebar-width: 22rem; }