Skip to content

Commit 9c91385

Browse files
committed
Use tokens from editor step
1 parent f79d50f commit 9c91385

File tree

7 files changed

+175
-21
lines changed

7 files changed

+175
-21
lines changed

packages/mdx/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"node-fetch": "^2.0.0",
3535
"remark-rehype": "^8.1.0",
3636
"unified": "^9.2.2",
37-
"unist-util-visit": "^2.0.0"
37+
"unist-util-visit": "^2.0.0",
38+
"unist-util-visit-parents": "^2.0.0"
3839
},
3940
"peerDependencies": {
4041
"react": "^16.8.3 || ^17 || ^18"

packages/mdx/src/index.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,17 @@
3737
.ch-section-link * {
3838
text-decoration: underline;
3939
text-decoration-style: dotted;
40+
text-decoration-thickness: 1px;
4041
text-decoration-color: var(
4142
--ch-code-foreground,
4243
currentColor
4344
);
4445
}
45-
4646
.ch-section-link[data-active="true"] {
4747
background-color: #bae6fd66;
4848
}
49+
50+
.ch-section-link[data-active="true"],
51+
.ch-section-link[data-active="true"] * {
52+
text-decoration-thickness: 1.5px;
53+
}

packages/mdx/src/plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ export function remarkCodeHike(
4141
}
4242

4343
try {
44-
await transformInlineCodes(tree, config)
4544
await transformPreviews(tree)
4645
await transformScrollycodings(tree, config)
4746
await transformSpotlights(tree, config)
4847
await transformSlideshows(tree, config)
4948
await transformSections(tree, config)
49+
await transformInlineCodes(tree, config)
5050
await transformEditorNodes(tree, config)
5151
await transformCodeNodes(tree, config)
5252
} catch (e) {

packages/mdx/src/plugin/inline-code.ts

Lines changed: 149 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import {
55
} from "./unist-utils"
66
import { Node } from "unist"
77
import visit from "unist-util-visit"
8+
import visitParents from "unist-util-visit-parents"
89
import { Parent } from "hast-util-to-estree"
910
import { highlight } from "@code-hike/highlighter"
11+
import { EditorStep } from "@code-hike/mini-editor"
12+
import { Code } from "@code-hike/utils"
1013

1114
export async function transformInlineCodes(
1215
tree: Node,
@@ -32,14 +35,18 @@ export async function transformInlineCodes(
3235
["mdxJsxFlowElement", "mdxJsxTextElement"],
3336
async (node: Parent) => {
3437
if (node.name === "CH.InlineCode") {
35-
const code = await highlight({
36-
code: node.children[0].value as string,
37-
lang: "jsx",
38-
theme,
39-
})
38+
const inlinedCode = node.children[0].value as string
39+
const lang = node.attributes?.lang
40+
4041
toJSX(node, {
4142
props: {
42-
code,
43+
code: await getCode(
44+
tree,
45+
node,
46+
inlinedCode,
47+
lang,
48+
theme
49+
),
4350
codeConfig: CH_CODE_CONFIG_PLACEHOLDER,
4451
},
4552
appendProps: true,
@@ -48,3 +55,139 @@ export async function transformInlineCodes(
4855
}
4956
)
5057
}
58+
59+
async function getCode(
60+
tree: Node,
61+
node: Parent,
62+
inlinedCode: string,
63+
lang: string | undefined,
64+
theme: any
65+
) {
66+
const ancestors = getAncestors(tree, node)
67+
const sectionNode = ancestors.find(
68+
n => n.data?.editorStep
69+
)
70+
71+
// if node isn't inside a section-like parent, use provided lang or "jsx"
72+
if (!sectionNode) {
73+
return await highlight({
74+
code: inlinedCode,
75+
lang: lang || "jsx",
76+
theme,
77+
})
78+
}
79+
80+
const editorStep = sectionNode.data
81+
.editorStep as any as EditorStep
82+
83+
// if the same code is present in the editor step, use it
84+
const existingCode = getExistingCode(
85+
editorStep.files,
86+
inlinedCode
87+
)
88+
89+
if (existingCode) {
90+
return existingCode
91+
}
92+
93+
// or else, try to guess the language from somewhere
94+
const activeFile =
95+
editorStep.files?.find(
96+
f => f.name === editorStep.northPanel?.active
97+
) || editorStep.files[0]
98+
99+
const activeLang = activeFile?.code?.lang
100+
101+
return await highlight({
102+
code: inlinedCode,
103+
lang: lang || activeLang || "jsx",
104+
theme,
105+
})
106+
}
107+
108+
function getAncestors(tree: Node, node: Node): Parent[] {
109+
let ancestors: Parent[] = []
110+
visitParents(tree, node, (node, nodeAncestors) => {
111+
ancestors = nodeAncestors
112+
})
113+
return ancestors
114+
}
115+
116+
function getExistingCode(
117+
files: EditorStep["files"] | undefined,
118+
inlinedCode: string
119+
): Code | undefined {
120+
if (!files) {
121+
return undefined
122+
}
123+
124+
for (const file of files) {
125+
for (const line of file.code.lines) {
126+
const lineContent = line.tokens
127+
.map(t => t.content)
128+
.join("")
129+
const index = lineContent.indexOf(inlinedCode)
130+
if (index !== -1) {
131+
const tokens = sliceTokens(
132+
line,
133+
index,
134+
inlinedCode.length
135+
)
136+
return { lang: file.code.lang, lines: [{ tokens }] }
137+
}
138+
}
139+
}
140+
return undefined
141+
}
142+
143+
function sliceTokens(
144+
line: Code["lines"][0],
145+
start: number,
146+
length: number
147+
) {
148+
const tokens = line.tokens
149+
let currentLength = 0
150+
151+
let headTokens = [] as Code["lines"][0]["tokens"]
152+
153+
for (let i = 0; i < tokens.length; i++) {
154+
if (currentLength === start) {
155+
headTokens = tokens.slice(i)
156+
break
157+
}
158+
if (currentLength + tokens[i].content.length > start) {
159+
const newToken = {
160+
...tokens[i],
161+
content: tokens[i].content.slice(
162+
start - currentLength
163+
),
164+
}
165+
headTokens = [newToken].concat(tokens.slice(i + 1))
166+
break
167+
}
168+
currentLength += tokens[i].content.length
169+
}
170+
171+
currentLength = 0
172+
for (let i = 0; i < headTokens.length; i++) {
173+
if (currentLength === length) {
174+
return headTokens.slice(0, i)
175+
}
176+
if (
177+
currentLength + headTokens[i].content.length >
178+
length
179+
) {
180+
const newToken = {
181+
...headTokens[i],
182+
content: headTokens[i].content.slice(
183+
0,
184+
length - currentLength
185+
),
186+
}
187+
188+
return headTokens.slice(0, i).concat([newToken])
189+
}
190+
currentLength += headTokens[i].content.length
191+
}
192+
return []
193+
}

packages/mdx/src/plugin/section.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,20 @@ async function transformSection(
2626
await visitAsync(
2727
node,
2828
["mdxJsxFlowElement", "code"],
29-
async (node, index, parent) => {
30-
if (isEditorNode(node)) {
29+
async (editorNode, index, parent) => {
30+
if (isEditorNode(editorNode)) {
3131
props = await mapAnyCodeNode(
32-
{ node, index, parent: parent! },
32+
{ node: editorNode, index, parent: parent! },
3333
config
3434
)
35-
toJSX(node, { name: "CH.SectionCode", props: {} })
35+
toJSX(editorNode, {
36+
name: "CH.SectionCode",
37+
props: {},
38+
})
3639
}
3740
}
3841
)
39-
42+
node.data = { editorStep: props }
4043
transformLinks(node)
4144

4245
if (props) {

packages/mdx/src/plugin/steps.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ export async function extractStepsInfo(
2929
steps[stepIndex] = steps[stepIndex] || { children: [] }
3030
const step = steps[stepIndex]
3131
if (!step.editorStep && isEditorNode(child)) {
32-
const {
33-
codeConfig,
34-
...editorStep
35-
} = await mapAnyCodeNode(
36-
{ node: child, parent, index: i },
37-
config
38-
)
32+
const { codeConfig, ...editorStep } =
33+
await mapAnyCodeNode(
34+
{ node: child, parent, index: i },
35+
config
36+
)
3937

4038
if (stepIndex === 0) {
4139
// for the header props, keep it as it is
@@ -58,6 +56,7 @@ export async function extractStepsInfo(
5856
return {
5957
type: "mdxJsxFlowElement",
6058
children: step.children,
59+
data: { editorStep: step.editorStep },
6160
}
6261
})
6362

packages/playground/content/section.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Lorem ipsum dolor sit amet.
55
<CH.Section>
66

77
Consectetur adipiscing elit, sed do eiusmod tempor [incididunt](focus://4:7) ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget.
8+
_`orem`_, _`sum`_
89

910
```js
1011
function lorem(ipsum, dolor) {
@@ -45,4 +46,6 @@ function lorem(ipsum, dolor) {
4546

4647
</CH.Code>
4748

49+
_`ackground-color: va`_
50+
4851
</CH.Section>

0 commit comments

Comments
 (0)