Skip to content

Commit

Permalink
support @ data close #65
Browse files Browse the repository at this point in the history
  • Loading branch information
JSer committed Apr 11, 2024
1 parent 9347ca3 commit 66b2af3
Show file tree
Hide file tree
Showing 17 changed files with 298 additions and 24 deletions.
17 changes: 17 additions & 0 deletions examples/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,23 @@ const Hello = "World!"
lang="js"
shakuEnabled
/>
<h3>Custom data attributes</h3>
<p>
You can also use <code>@data</code> to add custom data attributes to a
line, which could be useful if you are building something on top of
Shaku.
</p>
<CodeBlock
code={`// @data hello=world jser=dev !
// @data hello=world jser=dev
// @highlight
const Hello = "World!"
// ^
// [Open the dev console and inspect this line,]
// [you'll see this line is rendered with the custom data attributes]`}
lang="js"
shakuEnabled
/>
<$.h2 $textAlign="center">Dev Tools</$.h2>
<p>
We got some tools to understand how Shaku works, such as{" "}
Expand Down
1 change: 1 addition & 0 deletions examples/web/components/CodePreview/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const sanitize = (html: string) => {
mark: ["data-*", "class"],
details: ["class"],
summary: ["style"],
"*": ["data-*"],
},
});
};
16 changes: 16 additions & 0 deletions examples/web/components/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,22 @@ const Hello = "World!"
// [you'll see this line is rendered with the custom class names!]
\`\`\`
## Custom data attributes
You can also use \`@data\` to add custom data attributes to a
line, which could be useful if you are building something on top of
Shaku.
\`\`\`tsx annotate
// @data hello=world jser=dev !
// @data hello=world jser=dev
// @highlight
const Hello = "World!"
// ^
// [Open the dev console and inspect this line,]
// [you'll see this line is rendered with the custom data attributes!]
\`\`\`
## How to Use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,59 @@ exports[`parseLine() can parse comment lines > @class abc-1 efg-2 h123! 1`] = `
}
`;

exports[`parseLine() can parse comment lines > @data abc=123 1`] = `
{
"config": {
"entries": [
{
"key": "abc",
"value": "123",
},
],
"isEscaped": false,
},
"type": "DirectiveData",
}
`;

exports[`parseLine() can parse comment lines > @data abc=123 ed1=abc 1`] = `
{
"config": {
"entries": [
{
"key": "abc",
"value": "123",
},
{
"key": "ed1",
"value": "abc",
},
],
"isEscaped": false,
},
"type": "DirectiveData",
}
`;

exports[`parseLine() can parse comment lines > @data abc=123 ed1=abc ! 1`] = `
{
"config": {
"entries": [
{
"key": "abc",
"value": "123",
},
{
"key": "ed1",
"value": "abc",
},
],
"isEscaped": true,
},
"type": "DirectiveData",
}
`;

exports[`parseLine() can parse comment lines > @diff 1`] = `null`;

exports[`parseLine() can parse comment lines > @diff - ^ 1`] = `
Expand Down
3 changes: 3 additions & 0 deletions packages/shaku-code-annotate-core/src/__tests__/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ describe("parseLine() can parse comment lines", () => {
"@class abc efg h123 ",
"@class abc efg h123!",
"@class abc-1 efg-2 h123!",
"@data abc=123",
"@data abc=123 ed1=abc ",
"@data abc=123 ed1=abc !",
];
for (const input of inputs) {
test(input, () => {
Expand Down
44 changes: 43 additions & 1 deletion packages/shaku-code-annotate-core/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,17 @@ export type ShakuDirectiveClass = {
const RegShakuDirectiveClass =
/^(?<leadingSpaces>\s*)@class\s+(?<classNames>([a-zA-Z0-9 \-_]+))\s*(?<escape>!?)\s*$/;

export type ShakuDirectiveData = {
type: "DirectiveData";
config: {
isEscaped: boolean;
entries: Array<{ key: string; value: string }>;
};
};

const RegShakuDirectiveData =
/^(?<leadingSpaces>\s*)@data\s+(?<entries>([a-zA-Z0-9 \-=_]+))\s*(?<escape>!?)\s*$/;

export type ShakuLine =
| ShakuDirectiveUnderline
| ShakuAnnotationLine
Expand All @@ -151,7 +162,8 @@ export type ShakuLine =
| ShakuDirectiveFocus
| ShakuDirectiveHighlightInline
| ShakuDirectiveDiff
| ShakuDirectiveClass;
| ShakuDirectiveClass
| ShakuDirectiveData;

export const parseLine = (line: string): ShakuLine | null => {
const matchShakuDirectiveUnderlineSolid = line.match(
Expand Down Expand Up @@ -347,6 +359,32 @@ export const parseLine = (line: string): ShakuLine | null => {
};
}

const matchShakuDirectiveData = line.match(RegShakuDirectiveData);
if (matchShakuDirectiveData) {
const entriesStr = matchShakuDirectiveData.groups?.entries ?? "";
const entries = filterNonNull(
entriesStr
.trim()
.split(/\s+/)
.map((entry) => {
const segs = entry.split("=");
if (segs.length !== 2) return null;
return {
key: segs[0],
value: segs[1],
};
})
);

return {
type: "DirectiveData",
config: {
isEscaped: !!matchShakuDirectiveData.groups?.escape,
entries,
},
};
}

return null;
};

Expand Down Expand Up @@ -445,3 +483,7 @@ function getCanonicalMark(mark: string) {
if (mark === "^") return "end";
return mark;
}

function filterNonNull<T>(arr: (T | null)[]): T[] {
return arr.filter((item): item is T => item !== null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ exports[`codeToHtml() + raw HTML 4`] = `
}
`;

exports[`codeToHtml() + raw HTML 5`] = `
{
"html": "<pre class=\\"shiki shaku github-light\\" style=\\"color:#24292e;background-color:#fff\\"><div class=\\"code-container\\"><code><div class=\\"line\\"></div><div class=\\"line\\"><span style=\\"color: #D73A49\\">function</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">useSomeEffect</span><span style=\\"color: #24292E\\">({</span><span style=\\"color: #E36209\\">blog</span><span style=\\"color: #24292E\\">}) {</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">useEffect</span><span style=\\"color: #24292E\\">(() </span><span style=\\"color: #D73A49\\">=&gt;</span><span style=\\"color: #24292E\\"> {</span></div><div class=\\"line\\" data-a=\\"1\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #D73A49\\">return</span><span style=\\"color: #24292E\\"> () </span><span style=\\"color: #D73A49\\">=&gt;</span><span style=\\"color: #24292E\\"> {</span></div><div class=\\"line\\" data-a-b-c=\\"1-2-3\\" data-beg-1=\\"hello-2\\"><span style=\\"color: #24292E\\"> location.href </span><span style=\\"color: #D73A49\\">=</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #032F62\\">&#039;https://jser.dev&#039;</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> }</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> }, [blog])</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\">}</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span></div></code></div></pre>",
"skipped": false,
}
`;

exports[`codeToHtml() 1`] = `
{
"html": "<pre class=\\"shiki shaku github-light\\" style=\\"color:#24292e;background-color:#fff\\"><div class=\\"code-container\\"><code><div class=\\"line\\"></div><div class=\\"line\\"><span style=\\"color: #D73A49\\">function</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">ChatRoom</span><span style=\\"color: #24292E\\">({ </span><span style=\\"color: #E36209\\">roomId</span><span style=\\"color: #24292E\\"> }) {</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #D73A49\\">const</span><span style=\\"color: #24292E\\"> [</span><span style=\\"color: #005CC5\\">serverUrl</span><span style=\\"color: #24292E\\">, </span><span style=\\"color: #005CC5\\">setServerUrl</span><span style=\\"color: #24292E\\">] </span><span style=\\"color: #D73A49\\">=</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">useState</span><span style=\\"color: #24292E\\">(</span><span style=\\"color: #032F62\\">&#039;https://localhost:1234&#039;</span><span style=\\"color: #24292E\\">);</span></div><div class=\\"shaku-callout\\" style=\\"left:5ch\\"><span class=\\"shaku-callout-arrow\\" style=\\"left:6ch\\"></span>&lt;a href=&quot;https:jser.dev&quot;&gt;jser.dev&lt;/a&gt;</div><div class=\\"line\\"></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">useEffect</span><span style=\\"color: #24292E\\">(() </span><span style=\\"color: #D73A49\\">=&gt;</span><span style=\\"color: #24292E\\"> {</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><mark class=\\"shaku-inline-highlight\\" ><span style=\\"color: #D73A49\\">const</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #005CC5\\">connection</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #D73A49\\">=</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">createConnection</span><span style=\\"color: #24292E\\">(serverUrl, roomId);</span></mark></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><mark class=\\"shaku-inline-highlight\\" data-id=\\"1\\"><span style=\\"color: #24292E\\">connection.</span><span style=\\"color: #6F42C1\\">connect</span><span style=\\"color: #24292E\\">(</span></mark><span style=\\"color: #24292E\\">);</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #D73A49\\">return</span><span style=\\"color: #24292E\\"> () </span><span style=\\"color: #D73A49\\">=&gt;</span><span style=\\"color: #24292E\\"> {</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><mark class=\\"shaku-inline-highlight\\" data-id=\\"2\\"><span style=\\"color: #24292E\\">connection.</span><span style=\\"color: #6F42C1\\">disconnect</span><span style=\\"color: #24292E\\">()</span></mark><span style=\\"color: #24292E\\">;</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> };</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> }, </span><mark class=\\"shaku-inline-highlight\\" data-id=\\"3\\"><span style=\\"color: #24292E\\">[serverUrl, roomId])</span></mark><span style=\\"color: #24292E\\">;</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\">}</span></div><div class=\\"line\\"></div></code></div></pre>",
Expand Down Expand Up @@ -55,3 +62,10 @@ exports[`codeToHtml() 4`] = `
"skipped": false,
}
`;

exports[`codeToHtml() 5`] = `
{
"html": "<pre class=\\"shiki shaku github-light\\" style=\\"color:#24292e;background-color:#fff\\"><div class=\\"code-container\\"><code><div class=\\"line\\"></div><div class=\\"line\\"><span style=\\"color: #D73A49\\">function</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">useSomeEffect</span><span style=\\"color: #24292E\\">({</span><span style=\\"color: #E36209\\">blog</span><span style=\\"color: #24292E\\">}) {</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #6F42C1\\">useEffect</span><span style=\\"color: #24292E\\">(() </span><span style=\\"color: #D73A49\\">=&gt;</span><span style=\\"color: #24292E\\"> {</span></div><div class=\\"line\\" data-a=\\"1\\"><span style=\\"color: #24292E\\"> </span><span style=\\"color: #D73A49\\">return</span><span style=\\"color: #24292E\\"> () </span><span style=\\"color: #D73A49\\">=&gt;</span><span style=\\"color: #24292E\\"> {</span></div><div class=\\"line\\" data-a-b-c=\\"1-2-3\\" data-beg-1=\\"hello-2\\"><span style=\\"color: #24292E\\"> location.href </span><span style=\\"color: #D73A49\\">=</span><span style=\\"color: #24292E\\"> </span><span style=\\"color: #032F62\\">&#039;https://jser.dev&#039;</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> }</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> }, [blog])</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\">}</span></div><div class=\\"line\\"><span style=\\"color: #24292E\\"> </span></div></code></div></pre>",
"skipped": false,
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ function useSomeEffect({blog}) {
location.href = 'https://jser.dev'
}
}, [blog])
}
`,
`
function useSomeEffect({blog}) {
useEffect(() => {
// @data a=1
return () => {
// @data a-b-c=1-2-3 beg-1=hello-2
location.href = 'https://jser.dev'
}
}, [blog])
}
`,
];
Expand Down
23 changes: 21 additions & 2 deletions packages/shaku-code-annotate-shiki/src/codeToShakuHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
renderSourceLine,
renderSourceLineWithInlineHighlight,
} from "./render";
import { escapeHtml } from "./escapeHtml";

type StringLiteralUnion<T extends U, U = string> = T | (U & {});
interface CodeToShakuHtmlOptions {
Expand Down Expand Up @@ -94,6 +95,9 @@ export let codeToShakuHtml = function (
let diffBlock: false | "+" | "-" = false;
let isFoldBlock = false;
let classNamesForNextSourceLine = "";
let dataAttrsForNextSourceLine:
| false
| Array<{ key: string; value: string }> = false;

for (let i = 0; i < parsedLines.length; i++) {
const line = parsedLines[i];
Expand Down Expand Up @@ -167,7 +171,6 @@ export let codeToShakuHtml = function (
}
j += 1;
}
console.log(indent);
html += `<details class="shaku-expand"><summary style="margin-left:${indent}ch"><mark>{...}</mark></summary>`;
break;
}
Expand Down Expand Up @@ -310,6 +313,10 @@ export let codeToShakuHtml = function (
classNamesForNextSourceLine = shakuLine.config.classNames;
break;
}
case "DirectiveData": {
dataAttrsForNextSourceLine = shakuLine.config.entries;
break;
}
default:
assertsNever(shakuLine);
}
Expand All @@ -323,12 +330,14 @@ export let codeToShakuHtml = function (
const classNames = classNamesForNextSourceLine
? " " + classNamesForNextSourceLine
: "";
const dataAttrs = dataAttrsForNextSourceLine;

shouldHighlighNextSourceLine = false;
shouldFocusNextSourceLine = false;
shouldDimNextSourceLine = false;
diffNextSourceLine = false;
classNamesForNextSourceLine = "";
dataAttrsForNextSourceLine = false;

const sourceLine = line.type === "default" ? line.line : line.sourceLine;

Expand All @@ -341,7 +350,17 @@ export let codeToShakuHtml = function (
? " diff diff-delete"
: "";

const prefix = `<div class="line${highlightClass}${dimClass}${diffClass}${classNames}">`;
const classString = `line${highlightClass}${dimClass}${diffClass}${classNames}`;
const dataString = dataAttrs
? " " +
dataAttrs
.map(
({ key, value }) =>
`${escapeHtml(`data-${key}`)}="${escapeHtml(value)}"`
)
.join(" ")
: "";
const prefix = `<div class="${escapeHtml(classString)}"${dataString}>`;
html += prefix;

if (shakuDirectiveHighlightInline) {
Expand Down
4 changes: 4 additions & 0 deletions packages/shaku-code-annotate-shiki/src/defaultCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,10 @@ export default function Counter() {
//[Underline and callout!]
}
// @class clickable
// @data jser=dev
const blog = "https://jser.dev"
// @diff -
console.log('jser.dev')
return (
Expand Down
8 changes: 8 additions & 0 deletions packages/shaku-code-annotate-shiki/src/escapeHtml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function escapeHtml(html: string) {
return html
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
10 changes: 1 addition & 9 deletions packages/shaku-code-annotate-shiki/src/render.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IThemedToken } from "shiki";
import { ShakuDirectiveHighlightInline } from "shaku-code-annotate-core";
import { escapeHtml } from "./escapeHtml";

export const renderMarkStart = (id?: number) =>
`<mark class="shaku-inline-highlight" ${
Expand Down Expand Up @@ -120,12 +121,3 @@ export function renderSourceLine(sourceLine: IThemedToken[]) {
)
.join("");
}

function escapeHtml(html: string) {
return html
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
Loading

0 comments on commit 66b2af3

Please sign in to comment.