Skip to content

Commit dda86b5

Browse files
committed
Add support for line highlights
1 parent b83f3a3 commit dda86b5

File tree

6 files changed

+65
-6
lines changed

6 files changed

+65
-6
lines changed

content/snippets/demo/s/test-snippet.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,13 @@ This is a code block without a language. It doesn't appear very often, yet it's
9696
Let's also ensure lines wrap on their, as needed. Might come in handy!
9797
```
9898

99-
#### No language, yes title
99+
### Titles
100+
101+
```js title="This is a title"
102+
const x = 'this is a title';
103+
```
104+
105+
#### Without language
100106

101107
```text title="No language"
102108
No languge here
@@ -112,6 +118,15 @@ No languge here
112118
}
113119
```
114120

121+
### Highlighted lines
122+
123+
```js {1} {3-4}
124+
const x = 10;
125+
x += 5;
126+
const y = 20;
127+
console.log(x + y);
128+
```
129+
115130
## Other elements
116131

117132
There's a few more elements and some cases we should cover just to be safe.

src/astro/styles/_code.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ pre {
9292
min-height: calc(var(--line-height-normal) * 1em);
9393
// Apply the layout bleed for the inline padding.
9494
padding-inline: var(--layout-bleed-width);
95+
96+
&[data-line-highlight='true'] {
97+
background-color: var(--color-code-background-light);
98+
}
9599
}
96100
}
97101

src/lib/contentUtils/markdownParser/codeHighlighters/shiki.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { createOnigurumaEngine } from '@shikijs/engine-oniguruma';
77

88
import { transformerColorizedBrackets } from '@shikijs/colorized-brackets';
99
import { transformerColorSwatches } from './transformers/colorSwatches.js';
10+
import { transformerLineHighlights } from './transformers/lineHighlights.js';
1011

1112
const loadBundledThemes = () => {
1213
return {
@@ -65,12 +66,13 @@ export default class ShikiHighlighter {
6566
return 'shiki';
6667
}
6768

68-
static async highlightCode(code, language) {
69+
static async highlightCode(code, language, metadata) {
6970
const highlightedCode = await this.codeToHtml(code, {
7071
lang: language,
7172
theme: 'cosmos',
7273
transformers: [
7374
transformerColorSwatches(),
75+
transformerLineHighlights(metadata),
7476
transformerColorizedBrackets({
7577
themes: {
7678
cosmos: [
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Transforms `.line`s in the code block by adding the appropriate properties
3+
* to their nodes, if they are highlighted.
4+
*/
5+
export const transformerLineHighlights = ({
6+
highlightedLines = new Set(),
7+
} = {}) => {
8+
return {
9+
name: 'line-higlights',
10+
line(node, line) {
11+
// Skip if there are no highlighted lines.
12+
if (!highlightedLines.size) return node;
13+
14+
// If the line is highlighted, we need to add a data attribute to the
15+
// node, so that we can style it with CSS.
16+
if (highlightedLines.has(line)) {
17+
node.properties['data-line-highlight'] = 'true';
18+
}
19+
},
20+
};
21+
};

src/lib/contentUtils/markdownParser/codeHighlighters/utils/metaParser.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,18 @@ export const getMetaByKey = (meta, key) => {
263263
const result = meta.find(item => item.key === key);
264264
return result ?? null;
265265
};
266+
267+
export const getMetaString = (meta, key) => {
268+
const result = meta.find(item => item.key === key && item.type === 'string');
269+
return result ?? null;
270+
};
271+
272+
export const getMetaRanges = (meta, key) => {
273+
const ranges = meta.filter(item => item.key === key && item.type === 'range');
274+
const allRangeValues = ranges.reduce((acc, item) => {
275+
acc.push(...item.value);
276+
return acc;
277+
}, []);
278+
279+
return new Set(allRangeValues);
280+
};

src/lib/contentUtils/markdownParser/plugins/ast/highlightCode.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { visit } from 'unist-util-visit';
22
import {
33
parseMeta,
4-
getMetaByKey,
4+
getMetaString,
5+
getMetaRanges,
56
} from '#src/lib/contentUtils/markdownParser/codeHighlighters/utils/metaParser.js';
67

78
// Parse the language and title from the language string. Only supports
@@ -20,10 +21,11 @@ import {
2021
const createMetadataExtractor = languages => node => {
2122
const languageName = node.lang || 'text';
2223
const meta = parseMeta(node.meta || '');
23-
const title = getMetaByKey(meta, 'title')?.value || null;
24+
const title = getMetaString(meta, 'title')?.value || null;
2425
const languageStringLiteral = languages[languageName] || '';
26+
const highlightedLines = getMetaRanges(meta, null);
2527

26-
return { languageName, title, languageStringLiteral };
28+
return { languageName, title, languageStringLiteral, highlightedLines };
2729
};
2830

2931
/**
@@ -93,7 +95,7 @@ export const highlightCode = ({ grammars, codeHighlighter }) => {
9395
const { languageName } = metadata;
9496

9597
const promise = codeHighlighter
96-
.highlightCode(node.value, languageName)
98+
.highlightCode(node.value, languageName, metadata)
9799
.then(highlightedCode => {
98100
node.type = `html`;
99101
node.value = wrapHighlightedCode(highlightedCode, attributes);

0 commit comments

Comments
 (0)