@@ -5,8 +5,11 @@ import {
5
5
} from "./unist-utils"
6
6
import { Node } from "unist"
7
7
import visit from "unist-util-visit"
8
+ import visitParents from "unist-util-visit-parents"
8
9
import { Parent } from "hast-util-to-estree"
9
10
import { highlight } from "@code-hike/highlighter"
11
+ import { EditorStep } from "@code-hike/mini-editor"
12
+ import { Code } from "@code-hike/utils"
10
13
11
14
export async function transformInlineCodes (
12
15
tree : Node ,
@@ -32,14 +35,18 @@ export async function transformInlineCodes(
32
35
[ "mdxJsxFlowElement" , "mdxJsxTextElement" ] ,
33
36
async ( node : Parent ) => {
34
37
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
+
40
41
toJSX ( node , {
41
42
props : {
42
- code,
43
+ code : await getCode (
44
+ tree ,
45
+ node ,
46
+ inlinedCode ,
47
+ lang ,
48
+ theme
49
+ ) ,
43
50
codeConfig : CH_CODE_CONFIG_PLACEHOLDER ,
44
51
} ,
45
52
appendProps : true ,
@@ -48,3 +55,139 @@ export async function transformInlineCodes(
48
55
}
49
56
)
50
57
}
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
+ }
0 commit comments