diff --git a/README.md b/README.md index 7dd2da0..2c774cd 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,17 @@ - [Example: line numbers for multiline codeblock](#example-line-numbers-for-multiline-codeblock) - [Example: show prompts](#example-show-prompts) - [Example: highlight lines](#example-highlight-lines) - - [Example: add a caption to a codeblock](#example-add-a-caption-to-a-codeblock) - - [Example: configure aliases](#example-configure-aliases) + - [Example: codeblock with a caption](#example-codeblock-with-a-caption) + - [Example: aliases](#example-aliases) - [Example: custom header extension](#example-custom-header-extension) + - [Example: custom classname prefix](#example-custom-classname-prefix) + - [Example: custom marker](#example-custom-marker) - [Related](#related) - [License](#license) ## What’s this? -This package is a [unified](https://github.com/unifiedjs/unified) ([rehype](https://github.com/rehypejs/rehype)) plugin to highlight code with [Starry Night](https://github.com/wooorm/starry-night) in a markdown document. This results into syntax highlighting like what GitHub uses to highlight code. +This package is a [unified](https://github.com/unifiedjs/unified) ([rehype](https://github.com/rehypejs/rehype)) plugin to highlight code with [Starry Night](https://github.com/wooorm/starry-night) in a markdown document. The syntax highlighting mimics what GitHub's syntax highlighting color schemes. ## When should I use this? @@ -38,7 +40,7 @@ The following additonal features are also available: - line highlights - support for prompt - captions and language information -- highlighting inline `code` elements (through a custom directive) +- highlighting inline `code` elements ## Install @@ -84,7 +86,7 @@ import { unified } from "unified"; import remarkParse from "remark-parse"; import remarkRehype from "remark-rehype"; import rehypeStringify from "rehype-stringify"; -import rehypeStarryNight from "https://esm.sh/@microflash/rehype-starry-night"; +import rehypeStarryNight from "@microflash/rehype-starry-night"; main(); @@ -103,11 +105,11 @@ async function main() { Running that with `node example.js` yields: ```html -
html {
+html {
box-sizing: border-box;
text-size-adjust: 100%;
/* allow percentage based heights for the children */
@@ -121,12 +123,12 @@ Running that with `node example.js` yields:
### Support for inline `code` elements
-To highlight inline `code` elements, you can import [`rehype-starry-night-inline`](./src/rehype-starry-night-inline.js) plugin. This plugin relies on a custom directive which requires importing [`remark-directive`](https://github.com/remarkjs/remark-directive) and [`remark-code-directive`](./src/remark-code-directive.js).
+To highlight inline `code` elements, import [`rehype-starry-night-inline`](./src/rehype-starry-night-inline/index.js) plugin. This plugin relies on the language information injected by the [`remark-inline-code-lang`](./src/remark-inline-code-lang/index.js) plugin.
Say we have the following file `example.md`:
```md
-To print a greeting, use :code[console.log("Hello, world!");]{syntax=js}. When executed, it prints `Hello, world!`.
+To print a greeting, use `js> console.log("Hello, world!");`. When executed, it prints `Hello, world!`.
```
And our module `example.js` looks as follows:
@@ -134,19 +136,17 @@ And our module `example.js` looks as follows:
```js
import { unified } from "unified";
import remarkParse from "remark-parse";
-import remarkDirective from "remark-directive";
-import remarkCodeDirective from "https://esm.sh/@microflash/rehype-starry-night/remark-code-directive";
+import remarkInlineCodeLang from "@microflash/rehype-starry-night/remark-inline-code-lang";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
-import rehypeStarryNightInline from "https://esm.sh/@microflash/rehype-starry-night/rehype-starry-night-inline";
+import rehypeStarryNightInline from "@microflash/rehype-starry-night/rehype-starry-night-inline";
main();
async function main() {
const file = await unified()
.use(remarkParse)
- .use(remarkDirective)
- .use(remarkCodeDirective)
+ .use(remarkInlineCodeLang)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStarryNightInline)
.use(rehypeStringify, { allowDangerousHtml: true })
@@ -159,7 +159,7 @@ async function main() {
Running that with `node example.js` yields:
```html
- To print a greeting, use console.log("Hello, world!");
. When executed, it prints Hello, world!
.
+To print a greeting, use console.log("Hello, world!");
. When executed, it prints Hello, world!
.
```
![Highlighting inline code element](./samples/sample-2.png)
@@ -170,9 +170,11 @@ The default export is `rehypeStarryNight`. The following options are available.
- `aliases`: an object to alias languages to force syntax highlighting. By default, unknown languages are highlighted as plain text. Applicable to both `rehype-starry-night` and `rehype-starry-night-inline`.
- `grammars`: a list of [Starry Night](https://github.com/wooorm/starry-night) compatible grammar definitions. By default, all grammars provided by Starry Night are used. Applicable to both `rehype-starry-night` and `rehype-starry-night-inline`.
-- `headerExtensions`: a list of functions to customize the header. Applicable to only `rehype-starry-night`. By default, [language-extension](./src/hast-util-starry-night-header-language-extension.js) and [caption-extension](./src/hast-util-starry-night-header-caption-extension.js) are used. A header extension has access to the following arguments.
- - `headerOptions`: an object with a randomly generated `id` attached to the `pre` element, `metadata` containing the caption, list of highlighted lines, etc., and `language` tag specified on the code
+- `classNamePrefix` (default: `hl`): a prefix for the classNames for different elements of HTML generated by `rehype-starry-night` and `rehype-starry-night-inline`.
+- `headerExtensions`: a list of functions to customize the header. Applicable to only `rehype-starry-night`. By default, [language-extension](./src/rehype-starry-night/hast-util-starry-night-header-language-extension.js) and [caption-extension](./src/rehype-starry-night/hast-util-starry-night-header-caption-extension.js) are used. A header extension has access to the following arguments.
+ - `headerOptions`: an object with a randomly generated `id` attached to the `pre` element, `metadata` containing the caption, list of highlighted lines, etc., the `language` tag specified on the code, and the `classNamePrefix`
- `children`: an array of nodes contained in the header
+- `marker` (default: `> `): the marker for inline `code` element before which the language information is specified. Applicable to `remark-inline-code-lang`.
### Themes
@@ -187,17 +189,17 @@ There are multiple ways to support light and dark themes. Here's one way to do t
```css
:root {
/* light theme variables specific to rehype-starry-night plugin */
- --highlight-background-color: hsl(0, 0%, 100%);
- --highlight-border-color: hsl(208, 21%, 86%);
- --highlight-code-highlight: hsl(208, 19%, 82%);
+ --hl-background-color: hsl(0, 0%, 100%);
+ --hl-border-color: hsl(208, 21%, 86%);
+ --hl-code-highlight: hsl(208, 19%, 82%);
}
@media (prefers-color-scheme: dark) {
:root {
/* dark theme variables specific to rehype-starry-night plugin */
- --highlight-background-color: hsl(240, 20%, 2%);
- --highlight-border-color: hsl(208, 21%, 12%);
- --highlight-code-highlight: hsl(208, 19%, 13%);
+ --hl-background-color: hsl(240, 20%, 2%);
+ --hl-border-color: hsl(208, 21%, 12%);
+ --hl-code-highlight: hsl(208, 19%, 13%);
}
}
@@ -205,7 +207,7 @@ There are multiple ways to support light and dark themes. Here's one way to do t
@import "https://raw.githubusercontent.com/wooorm/starry-night/main/style/both.css";
/* import CSS specific to rehype-starry-night plugin */
-@import "https://raw.githubusercontent.com/Microflash/rehype-starry-night/main/index.css";
+@import "https://raw.githubusercontent.com/Microflash/rehype-starry-night/main/src/index.css";
```
> [!WARNING]
@@ -222,11 +224,11 @@ There are multiple ways to support light and dark themes. Here's one way to do t
The above codeblock will yield:
```html
-
-
- sh
+
+
+ sh
-docker ps -a
+docker ps -a
```
@@ -244,11 +246,11 @@ The above codeblock will yield:
The above codeblock will yield:
```html
-
-
- css
+
+
+ css
-* {
+* {
display: revert;
}
@@ -273,11 +275,11 @@ Sometimes you may want to show a prompt while displaying a command-line instruct
The above codeblock will yield:
```html
-
-
- sh
+
+
+ sh
-
+ curl localhost:8080/actuator/health
curl localhost:8080/actuator/health"status":"UP"}
{?name=Anya
curl localhost:8080/greeter!
@@ -293,7 +295,7 @@ The above codeblock will yield:
You can highlight multiple lines by specifying the line numbers (or even, range of line numbers) between curly braces in the codeblock metadata.
- ```sh {4-7} prompt{1}
+ ```sh {4..7} prompt{1}
aws --endpoint-url http://localhost:4566 s3api list-buckets
{
"Buckets": [
@@ -312,11 +314,11 @@ You can highlight multiple lines by specifying the line numbers (or even, range
The above codeblock will yield:
```html
- Hello, Anya
-
- sh
+
+
+ sh
-
+ aws --endpoint-url http://localhost:4566 s3api list-buckets
aws --endpoint-url http://localhost:4566 s3api list-buckets
{"Buckets": [
@@ -337,7 +339,7 @@ The above codeblock will yield:
Refer to the documentation of [fenceparser](https://github.com/Microflash/fenceparser) to learn about the additional ways in which you can specify the information about highlighted lines.
-### Example: add a caption to a codeblock
+### Example: codeblock with a caption
Captions are useful to describe the context of a piece of code.
@@ -352,12 +354,12 @@ Captions are useful to describe the context of a piece of code.
The above codeblock will yield:
```html
- {
-
- sh
-
+
+
+ sh
+
-
+ aws configure
aws configure
AWS Access Key ID [None]: gwen
AWS Secret Access Key [None]: stacy
@@ -368,9 +370,9 @@ The above codeblock will yield:
![Syntax Highlighting add a caption to a codeblock](./samples/sample-7.png)
-### Example: configure aliases
+### Example: aliases
-Although Starry Night [supports](https://github.com/wooorm/starry-night#languages=) a huge number of languages, it is not all encompassing. In such cases, you can configure aliases to force syntax highlighting a codeblock containing code in a language not yet supported by Starry Night.
+Although Starry Night [supports](https://github.com/wooorm/starry-night#languages=) a large number of languages, it is not all encompassing. In such cases, you can configure aliases to force the syntax highlighting on a codeblock containing code in a language not yet supported by Starry Night.
Say we have the following file `example.md`:
@@ -406,11 +408,11 @@ async function main() {
Running that with `node example.js` yields:
```html
- Default region name [None]: us-east-1
-
- xjm
+
+
+ xjm
-language = "en"
+language = "en"
customization = false
features = [ "io", "graphics", "compute" ]
@@ -436,7 +438,7 @@ import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
-import rehypeStarryNight from "https://esm.sh/@microflash/rehype-starry-night";
+import rehypeStarryNight from "@microflash/rehype-starry-night";
import rehypeStarryNightHeaderCaptionExtension from "@microflash/rehype-starry-night/header-caption-extension";
import rehypeStarryNightHeaderLanguageExtension from "@microflash/rehype-starry-night/header-language-extension";
@@ -454,7 +456,7 @@ async function main() {
children.push({
type: "element",
tagName: "button",
- properties: { className: ["highlight-copy"], for: headerOptions.id },
+ properties: { className: [`${headerOptions.classNamePrefix}-copy`], for: headerOptions.id },
children: [
{
type: "text",
@@ -475,18 +477,141 @@ async function main() {
Running that with `node example.js` yields:
```html
-
-
- html
-
+
+
+ html
+
-<mark>highlighted</mark>
+<mark>highlighted</mark>
```
![Syntax Highlighting with custom header extension](./samples/sample-9.png)
+### Example: custom classname prefix
+
+You can attach your own prefix on the classNames of HTML elements generated by the `rehype-starry-night` and `rehype-starry-night-inline` plugins.
+
+Say we have the following file `example.md`:
+
+ ```java
+ System.out.println("Hello, world!");
+ ```
+
+You can customize the className prefix as follows with `example.js`:
+
+```js
+import { unified } from "unified";
+import remarkParse from "remark-parse";
+import remarkRehype from "remark-rehype";
+import rehypeStringify from "rehype-stringify";
+import rehypeStarryNight from "@microflash/rehype-starry-night";
+
+main()
+
+async function main() {
+ const file = await unified()
+ .use(remarkParse)
+ .use(remarkRehype, { allowDangerousHtml: true })
+ .use(rehypeStarryNight, { classNamePrefix: "syntax" })
+ .use(rehypeStringify, { allowDangerousHtml: true })
+ .process(markdown);
+
+ console.log(String(file));
+}
+```
+
+Running that with `node example.js` yields:
+
+```html
+
+
+ java
+
+System.out.println("Hello, world!");
+
+
+```
+
+Similarly for inline `code` element, say we have the following file `example.md`:
+
+```md
+To remove the whitespace around a string, try `java> str.strip()`.
+```
+
+You can customize the className prefix as follows with `example.js`:
+
+```js
+import { unified } from "unified";
+import remarkParse from "remark-parse";
+import remarkInlineCodeLang from "@microflash/rehype-starry-night/remark-inline-code-lang";
+import remarkRehype from "remark-rehype";
+import rehypeStringify from "rehype-stringify";
+import rehypeStarryNightInline from "@microflash/rehype-starry-night/rehype-starry-night-inline";
+
+main();
+
+async function main() {
+ const file = await unified()
+ .use(remarkParse)
+ .use(remarkInlineCodeLang)
+ .use(remarkRehype, { allowDangerousHtml: true })
+ .use(rehypeStarryNightInline, { classNamePrefix: "syntax" })
+ .use(rehypeStringify, { allowDangerousHtml: true })
+ .process(markdown);
+
+ console.log(String(file));
+}
+```
+
+Running that with `node example.js` yields:
+
+```html
+To remove the whitespace around a string, try str.strip()
.
+```
+
+### Example: custom marker
+
+You can configure a custom marker for inline `code` element to inject the language information. For example, say you want to annotate your inline `code` element with `: ` instead of the default `> ` marker, as shown in the following file `example.md`:
+
+```md
+To specify the language direction, use `html: مرحبا`.
+```
+
+You can customize the marker as follows with `example.js`:
+
+```js
+import { unified } from "unified";
+import remarkParse from "remark-parse";
+import remarkInlineCodeLang from "@microflash/rehype-starry-night/remark-inline-code-lang";
+import remarkRehype from "remark-rehype";
+import rehypeStringify from "rehype-stringify";
+import rehypeStarryNightInline from "@microflash/rehype-starry-night/rehype-starry-night-inline";
+
+main();
+
+async function main() {
+ const file = await unified()
+ .use(remarkParse)
+ .use(remarkInlineCodeLang, { marker: ": " })
+ .use(remarkRehype, { allowDangerousHtml: true })
+ .use(rehypeStarryNightInline)
+ .use(rehypeStringify, { allowDangerousHtml: true })
+ .process(markdown);
+
+ console.log(String(file));
+}
+```
+
+Running that with `node example.js` yields:
+
+```html
+To specify the language direction, use <span dir="rtl">مرحبا</span>
.
+```
+
+![Syntax Highlighting through language information using custom marker](./samples/sample-10.png)
+
## Related
- [`rehype-highlight`](https://github.com/rehypejs/rehype-highlight) — highlight code with [highlight.js](https://github.com/isagalaev/highlight.js) (through [lowlight](https://github.com/wooorm/lowlight))
diff --git a/samples/sample-1.png b/samples/sample-1.png
index e41eba3..43e1a3c 100644
Binary files a/samples/sample-1.png and b/samples/sample-1.png differ
diff --git a/samples/sample-10.png b/samples/sample-10.png
new file mode 100644
index 0000000..09db0f0
Binary files /dev/null and b/samples/sample-10.png differ
diff --git a/samples/sample-2.png b/samples/sample-2.png
index b72bee8..c862a4e 100644
Binary files a/samples/sample-2.png and b/samples/sample-2.png differ
diff --git a/samples/sample-3.png b/samples/sample-3.png
index 904df94..effc0b7 100644
Binary files a/samples/sample-3.png and b/samples/sample-3.png differ
diff --git a/samples/sample-4.png b/samples/sample-4.png
index 1899751..3130faf 100644
Binary files a/samples/sample-4.png and b/samples/sample-4.png differ
diff --git a/samples/sample-5.png b/samples/sample-5.png
index 3c6c1d0..cacbe1a 100644
Binary files a/samples/sample-5.png and b/samples/sample-5.png differ
diff --git a/samples/sample-6.png b/samples/sample-6.png
index ff8ccab..41d08c4 100644
Binary files a/samples/sample-6.png and b/samples/sample-6.png differ
diff --git a/samples/sample-7.png b/samples/sample-7.png
index 050c651..afc54dc 100644
Binary files a/samples/sample-7.png and b/samples/sample-7.png differ
diff --git a/samples/sample-8.png b/samples/sample-8.png
index 21093a6..7e1be68 100644
Binary files a/samples/sample-8.png and b/samples/sample-8.png differ
diff --git a/samples/sample-9.png b/samples/sample-9.png
index 0e57a36..4e5a07a 100644
Binary files a/samples/sample-9.png and b/samples/sample-9.png differ
diff --git a/src/index.css b/src/index.css
index 8b75bb2..7c6ae64 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,27 +1,27 @@
-.highlight {
- --hlBackgroundColor: var(--highlight-background-color, hsl(240, 20%, 2%));
- --hlBorderThickness: var(--highlight-border-thickness, 1px);
- --hlBorderColor: var(--highlight-border-color, hsl(208, 21%, 12%));
- --hlBorderRadius: var(--highlight-border-radius, 12px);
+.hl {
+ --hlBackgroundColor: var(--hl-background-color, hsl(240, 20%, 2%));
+ --hlBorderThickness: var(--hl-border-thickness, 1px);
+ --hlBorderColor: var(--hl-border-color, hsl(208, 21%, 12%));
+ --hlBorderRadius: var(--hl-border-radius, 12px);
--hlFontFamilyMono: ui-monospace, SFMono-Regular, "Roboto Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
- --hlFontFamilyCode: var(--highlight-font-mono, var(--hlFontFamilyMono));
- --hlCodeHighlight: var(--highlight-code-highlight, hsl(208, 19%, 13%));
- --hlLineNumberColor: var(--highlight-color-line-number, var(--color-prettylights-syntax-sublimelinter-gutter-mark));
- --hlLineNumberColorHighlighted: var(--highlight-color-line-number-highlighted, var(--color-prettylights-syntax-comment));
+ --hlFontFamilyCode: var(--hl-font-mono, var(--hlFontFamilyMono));
+ --hlCodeHighlight: var(--hl-code-highlight, hsl(208, 19%, 13%));
+ --hlLineNumberColor: var(--hl-color-line-number, var(--color-prettylights-syntax-sublimelinter-gutter-mark));
+ --hlLineNumberColorHighlighted: var(--hl-color-line-number-highlighted, var(--color-prettylights-syntax-comment));
background-color: var(--hlBackgroundColor);
border: var(--hlBorderThickness) solid var(--hlBorderColor);
border-radius: var(--hlBorderRadius);
}
-.highlight:focus-within {
+.hl:focus-within {
outline-color: var(--hlBorderColor);
outline-style: solid;
outline-width: var(--hlBorderThickness);
outline-offset: -0.125em;
}
-.highlight-header {
+.hl-header {
display: flex;
align-items: center;
gap: 1ch;
@@ -30,59 +30,59 @@
font-size: 0.75em;
}
-.highlight-language {
+.hl-language {
font-family: var(--hlFontFamilyCode);
background-color: var(--hlCodeHighlight);
padding: 0.75ch 1ch;
border-radius: calc(var(--hlBorderRadius) / 1.5);
}
-.highlight pre {
+.hl pre {
background-color: transparent;
border: none;
overflow-x: auto;
}
-.highlight pre>code {
+.hl pre>code {
display: grid;
padding: 1em 0;
cursor: auto;
touch-action: auto;
}
-.highlight pre .line {
+.hl pre .line {
border-left: 2px solid transparent;
padding-left: 1ch;
padding-right: 1ch;
}
-.highlight pre .line:focus:not(:focus-visible),
-.highlight pre .line:hover {
+.hl pre .line:focus:not(:focus-visible),
+.hl pre .line:hover {
background-color: var(--hlCodeHighlight);
}
-.highlight pre .line:focus:not(:focus-visible) .line-number,
-.highlight pre .line:hover .line-number {
+.hl pre .line:focus:not(:focus-visible) .line-number,
+.hl pre .line:hover .line-number {
color: var(--hlLineNumberColorHighlighted);
}
-.highlight pre .line .line-number {
+.hl pre .line .line-number {
user-select: none;
margin-right: 1em;
text-align: right;
color: var(--hlLineNumberColor);
}
-.highlight pre .line[data-highlighted] {
+.hl pre .line[data-highlighted] {
background-color: var(--hlCodeHighlight);
border-left-color: var(--hlLineNumberColorHighlighted);
}
-.highlight pre .line[data-highlighted] .line-number {
+.hl pre .line[data-highlighted] .line-number {
color: var(--hlLineNumberColorHighlighted);
}
-.highlight pre .line .line-prompt::before {
+.hl pre .line .line-prompt::before {
margin-right: 1ch;
vertical-align: middle;
content: "";
@@ -102,9 +102,9 @@
}
code.hl-inline {
- --hlBackgroundColorInline: var(--highlight-background-color-inline, hsl(208, 21%, 14%));
+ --hlBackgroundColorInline: var(--hl-background-color-inline, hsl(208, 21%, 14%));
- font-family: var(--highlight-font-mono);
+ font-family: var(--hl-font-mono);
background-color: var(--hlBackgroundColorInline);
padding-left: 0.5ch;
padding-right: 0.5ch;