Skip to content

Commit 35353c7

Browse files
committed
trying single code block highlight again. why is it blue???
1 parent 45a3710 commit 35353c7

File tree

3 files changed

+69
-34
lines changed

3 files changed

+69
-34
lines changed

astro.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import starlight from "@astrojs/starlight";
22
import { defineConfig } from "astro/config";
33
import { generateCliOptionsIntegration } from "./src/fetchReadme";
44
import smartypants from "remark-smartypants";
5+
import { pluginCodeMarkerAnchors } from "./src/CodeMarkerAnchorPlugin.mjs";
56

67
// https://astro.build/config
78
export default defineConfig({
@@ -17,6 +18,7 @@ export default defineConfig({
1718
generateCliOptionsIntegration("src/content/docs/guides/_cli.md"),
1819
starlight({
1920
expressiveCode: {
21+
plugins: [pluginCodeMarkerAnchors()],
2022
themes: ["catppuccin-frappe", "catppuccin-latte"],
2123
},
2224
title: "Docs",

src/CodeMarkerAnchorPlugin.mjs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* [Expressive code plugin] for transforming [markers][] beginning with `#`
3+
* into anchor links. The marker text is replaced with only `#` and
4+
* the marker becomes a hyperlink to the anchor itself.
5+
*
6+
* This allows creating links to certain named positions within a code block.
7+
*
8+
* [markers]: https://expressive-code.com/key-features/text-markers/
9+
* [Expressive code plugin]: https://expressive-code.com/reference/plugin-hooks/
10+
*/
11+
export function pluginCodeMarkerAnchors() {
12+
/** @type {import('astro-expressive-code').ExpressiveCodePlugin} */
13+
const plugin = {
14+
name: "CodeMarkerAnchorPlugin",
15+
hooks: {
16+
// first, markers beginning with `#` are identified. the anchor ID is
17+
// stored, then the text is replaced with just `#`.
18+
postprocessAnnotations: async (x) => {
19+
for (const line of x.codeBlock.getLines()) {
20+
for (const annot of line.getAnnotations()) {
21+
if (annot.markerType !== "mark") continue;
22+
if (annot.label?.startsWith("#")) {
23+
// NOTE: this is kind of hacky, it's adding a new field into the
24+
// TextMarkerAnnotation class.
25+
annot.anchor = annot.label.replace("#", "");
26+
annot.label = "#";
27+
}
28+
}
29+
}
30+
},
31+
32+
// secondly, rendered markers with recorded anchors are turned into
33+
// hyperlinks with id attributes.
34+
postprocessRenderedLine: async (x) => {
35+
const annot = x.line.getAnnotations().find((x) => !!x.anchor);
36+
if (!annot) return;
37+
38+
const lineAst = x.renderData.lineAst;
39+
40+
lineAst.tagName = "a";
41+
lineAst.properties.id = annot.anchor;
42+
lineAst.properties.href = `#${annot.anchor}`;
43+
lineAst.properties.style = `text-decoration-line: none;${lineAst.properties.style ?? ""}`;
44+
},
45+
},
46+
};
47+
48+
return plugin;
49+
}

src/fetchReadme.ts

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,38 +53,22 @@ function* generateMarkdown(lines: string[]) {
5353
const bodyRegex = /^ (.*)/;
5454
const defaultValuesRegex = /^\[(default|possible values|env): (.*)\]$/;
5555

56-
let match;
57-
for (const line of lines) {
58-
if (line.match(usageRegex)) {
59-
yield '```';
60-
yield line;
61-
yield '```';
62-
63-
} else if (line.match(headingRegex)) {
64-
yield "## " + escapeMarkdown(line.replace(/:$/, ''));
65-
66-
} else if (match = line.match(optionRegex)) {
67-
const option = escapeMarkdown(match[0]).trim();
68-
const longOption = line.replace(/-[^-],/, '');
69-
yield `### ${option}`;
70-
yield '';
71-
yield '```bash';
72-
yield `lychee ${longOption.trimStart()}`;
73-
yield '```';
74-
75-
} else if (match = line.match(bodyRegex)) {
76-
const line = match[1];
77-
if (match = line.match(defaultValuesRegex)) {
78-
yield `**${match[1]}**: ${match[2]}`;
79-
yield '';
80-
} else {
81-
yield ' ' + line;
82-
}
83-
84-
} else {
85-
yield line;
56+
const markers: {[l: string]: number} = {};
57+
for (const [i, line] of lines.entries()) {
58+
const match = line.match(optionRegex);
59+
if (match) {
60+
markers[`#${match[0].trim()}`] = i + 1;
8661
}
8762
}
63+
64+
for (const line of Object.keys(markers)) {
65+
yield '### ' + line.replace('#', '');
66+
yield '';
67+
}
68+
69+
yield '```text ' + Object.entries(markers).map(([l, i]) => JSON.stringify({[l]: i})).join(' ')
70+
yield* lines;
71+
yield '```';
8872
}
8973

9074
export async function generateCliOptionsMarkdown() {
@@ -94,10 +78,10 @@ export async function generateCliOptionsMarkdown() {
9478
const rawUsageText = extractHelpFromReadme(await readme.text());
9579
const usageText = [...generateMarkdown(splitLines(rawUsageText))].join("\n");
9680

97-
assert(usageText.match('\n## Options\n'), 'options heading missing, check headingRegex');
98-
assert(usageText.match('\n### --dump\n'), '--dump heading missing, check optionRegex');
99-
assert(usageText.match('\n### --root-dir\n'), '--root-dir heading missing, check optionRegex');
100-
assert(usageText.match('\n Inputs for link checking'), 'expected body text missing, check bodyRegex');
81+
// assert(usageText.match('\n## Options\n'), 'options heading missing, check headingRegex');
82+
// assert(usageText.match('\n### --dump\n'), '--dump heading missing, check optionRegex');
83+
// assert(usageText.match('\n### --root-dir\n'), '--root-dir heading missing, check optionRegex');
84+
// assert(usageText.match('\n Inputs for link checking'), 'expected body text missing, check bodyRegex');
10185

10286
return usageText;
10387
}

0 commit comments

Comments
 (0)