Skip to content

Commit 870881a

Browse files
committed
fix: enhance embed token handling in paragraphs and table cells
1 parent f7418f4 commit 870881a

5 files changed

Lines changed: 463 additions & 249 deletions

File tree

‎src/core/render/compiler/paragraph.js‎

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,48 @@
11
import{helperashelperTpl}from'../tpl.js';
22

3+
functionrenderParagraphText(text){
4+
if(text.startsWith('!>')){
5+
returnhelperTpl('callout important',text);
6+
}
7+
if(text.startsWith('?>')){
8+
returnhelperTpl('callout tip',text);
9+
}
10+
return/* html */`<p>${text}</p>`;
11+
}
12+
313
exportconstparagraphCompiler=({ renderer })=>
4-
(renderer.paragraph=function({ tokens }){
5-
consttext=this.parser.parseInline(tokens);
14+
(renderer.paragraph=function({ tokens, embedTokenMap }){
615
letresult;
716

8-
if(text.startsWith('!&gt;')){
9-
result=helperTpl('callout important',text);
10-
}elseif(text.startsWith('?&gt;')){
11-
result=helperTpl('callout tip',text);
17+
if(embedTokenMap&&tokens?.length){
18+
// Keep original inline order: plain text/link tokens stay inline, include links are replaced.
19+
constparts=[];
20+
letinlineBuffer=[];
21+
22+
constflushInlineBuffer=()=>{
23+
if(!inlineBuffer.length){
24+
return;
25+
}
26+
consttext=this.parser.parseInline(inlineBuffer);
27+
parts.push(renderParagraphText(text));
28+
inlineBuffer=[];
29+
};
30+
31+
tokens.forEach((inlineToken,inlineIndex)=>{
32+
constembedToken=embedTokenMap[inlineIndex];
33+
if(embedToken?.length){
34+
flushInlineBuffer();
35+
parts.push(this.parser.parse(embedToken));
36+
}else{
37+
inlineBuffer.push(inlineToken);
38+
}
39+
});
40+
41+
flushInlineBuffer();
42+
result=parts.join('');
1243
}else{
13-
result=/* html */`<p>${text}</p>`;
44+
consttext=this.parser.parseInline(tokens);
45+
result=renderParagraphText(text);
1446
}
1547

1648
returnresult;

‎src/core/render/compiler/tableCell.js‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@ export const tableCellCompiler = ({ renderer }) =>
22
(renderer.tablecell=function(token){
33
letcontent;
44

5-
if(token.embedTokens&&token.embedTokens.length>0){
5+
if(token.embedTokenMap&&token.tokens?.length){
6+
// Preserve mixed content order: render inline tokens, replacing include links by position.
7+
content='';
8+
token.tokens.forEach((inlineToken,inlineIndex)=>{
9+
constembedToken=token.embedTokenMap[inlineIndex];
10+
if(embedToken?.length){
11+
content+=this.parser.parse(embedToken);
12+
}else{
13+
content+=this.parser.parseInline([inlineToken]);
14+
}
15+
});
16+
}elseif(token.embedTokens&&token.embedTokens.length>0){
617
content=this.parser.parse(token.embedTokens);
718
}else{
819
content=this.parser.parseInline(token.tokens);

‎src/core/render/embed.js‎

Lines changed: 102 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,13 @@ function extractFragmentContent(text, fragment, fullLine) {
3333
}
3434

3535
functionwalkFetchEmbed({ embedTokens, compile, fetch },cb){
36-
lettoken;
37-
letstep=0;
38-
letcount=0;
39-
4036
if(!embedTokens.length){
4137
returncb({});
4238
}
4339

44-
while((token=embedTokens[step++])){
45-
constcurrentToken=token;
40+
constprocessStep=step=>{
41+
constcurrentToken=embedTokens[step];
4642

47-
// eslint-disable-next-line no-loop-func
4843
constnext=text=>{
4944
letembedToken;
5045
if(text){
@@ -119,17 +114,21 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
119114
tokenRef: currentToken.tokenRef,
120115
});
121116

122-
if(++count>=embedTokens.length){
117+
if(step+1>=embedTokens.length){
123118
cb({});
119+
}else{
120+
processStep(step+1);
124121
}
125122
};
126123

127-
if(token.embed.url){
128-
get(token.embed.url).then(next);
124+
if(currentToken.embed.url){
125+
get(currentToken.embed.url).then(next);
129126
}else{
130-
next(token.embed.html);
127+
next(currentToken.embed.html);
131128
}
132-
}
129+
};
130+
131+
processStep(0);
133132
}
134133

135134
exportfunctionprerenderEmbed({ compiler, raw ='', fetch },done){
@@ -143,46 +142,56 @@ export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
143142
constcompile=compiler._marked;
144143
lettokens=compile.lexer(raw);
145144
constembedTokens=[];
146-
constlinkRE=compile.Lexer.rules.inline.normal.link;
147145
constlinks=tokens.links;
148146

149-
constlinkMatcher=newRegExp(linkRE.source,'g');
150-
151147
tokens.forEach((token,index)=>{
152148
if(token.type==='paragraph'){
153-
token.text=token.text.replace(
154-
linkMatcher,
155-
(src,filename,href,title)=>{
156-
constembed=compiler.compileEmbed(href,title);
149+
(token.tokens||[]).forEach(
150+
(
151+
/** @type {{ type: string; href: any; title: any; }} */inlineToken,
152+
inlineIndex,
153+
)=>{
154+
if(inlineToken.type!=='link'){
155+
return;
156+
}
157+
158+
constembed=compiler.compileEmbed(
159+
inlineToken.href,
160+
inlineToken.title,
161+
);
157162
if(embed){
158163
embedTokens.push({
159164
index,
160165
tokenRef: token,
166+
inlineIndex,
161167
embed,
162168
});
163169
}
164-
returnsrc;
165170
},
166171
);
167172
}elseif(token.type==='table'){
168173
token.rows.forEach((row,rowIndex)=>{
169174
row.forEach((cell,cellIndex)=>{
170-
cell.text=cell.text.replace(
171-
linkMatcher,
172-
(src,filename,href,title)=>{
173-
constembed=compiler.compileEmbed(href,title);
174-
if(embed){
175-
embedTokens.push({
176-
index,
177-
tokenRef: token,
178-
rowIndex,
179-
cellIndex,
180-
embed,
181-
});
182-
}
183-
returnsrc;
184-
},
185-
);
175+
(cell.tokens||[]).forEach((inlineToken,inlineIndex)=>{
176+
if(inlineToken.type!=='link'){
177+
return;
178+
}
179+
180+
constembed=compiler.compileEmbed(
181+
inlineToken.href,
182+
inlineToken.title,
183+
);
184+
if(embed){
185+
embedTokens.push({
186+
index,
187+
tokenRef: token,
188+
rowIndex,
189+
cellIndex,
190+
inlineIndex,
191+
embed,
192+
});
193+
}
194+
});
186195
});
187196
});
188197
}
@@ -192,30 +201,73 @@ export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
192201
// so that we know where to insert the embedded tokens as they
193202
// are returned
194203
constmoves=[];
204+
consttokenInsertState=newWeakMap();
195205
walkFetchEmbed(
196206
{ compile, embedTokens, fetch },
197207
({ embedToken, token, rowIndex, cellIndex, tokenRef })=>{
198208
if(token){
209+
Object.assign(links,embedToken.links);
210+
199211
if(typeofrowIndex==='number'&&typeofcellIndex==='number'){
200212
constcell=tokenRef.rows[rowIndex][cellIndex];
213+
if(typeoftoken.inlineIndex==='number'){
214+
cell.embedTokenMap||={};
215+
constexisting=cell.embedTokenMap[token.inlineIndex];
216+
cell.embedTokenMap[token.inlineIndex]=existing
217+
? existing.concat(embedToken)
218+
: embedToken;
219+
}
201220

202-
cell.embedTokens=embedToken;
221+
// Keep the flattened array for backward compatibility with older render paths.
222+
if(cell.embedTokens&&cell.embedTokens.length){
223+
cell.embedTokens=cell.embedTokens.concat(embedToken);
224+
}else{
225+
cell.embedTokens=embedToken;
226+
}
227+
}elseif(tokenRef.type==='paragraph'){
228+
if(typeoftoken.inlineIndex==='number'){
229+
tokenRef.embedTokenMap||={};
230+
constexisting=tokenRef.embedTokenMap[token.inlineIndex];
231+
tokenRef.embedTokenMap[token.inlineIndex]=existing
232+
? existing.concat(embedToken)
233+
: embedToken;
234+
}
235+
236+
// Keep a flattened form as a fallback for custom renderers.
237+
if(tokenRef.embedTokens&&tokenRef.embedTokens.length){
238+
tokenRef.embedTokens=tokenRef.embedTokens.concat(embedToken);
239+
}else{
240+
tokenRef.embedTokens=embedToken;
241+
}
203242
}else{
204-
// iterate through the array of previously inserted tokens
205-
// to determine where the current embedded tokens should be inserted
206-
letindex=token.index;
207-
moves.forEach(pos=>{
208-
if(index>pos.start){
209-
index+=pos.length;
210-
}
211-
});
243+
conststate=tokenInsertState.get(tokenRef);
212244

213-
Object.assign(links,embedToken.links);
245+
if(state){
246+
constinsertAt=state.nextIndex;
214247

215-
tokens=tokens
216-
.slice(0,index)
217-
.concat(embedToken,tokens.slice(index+1));
218-
moves.push({start: index,length: embedToken.length-1});
248+
tokens=tokens
249+
.slice(0,insertAt)
250+
.concat(embedToken,tokens.slice(insertAt));
251+
moves.push({start: insertAt,delta: embedToken.length});
252+
state.nextIndex=insertAt+embedToken.length;
253+
}else{
254+
// iterate through the array of previously inserted tokens
255+
// to determine where the current embedded tokens should be inserted
256+
letindex=token.index;
257+
moves.forEach(pos=>{
258+
if(index>pos.start){
259+
index+=pos.delta;
260+
}
261+
});
262+
263+
tokens=tokens
264+
.slice(0,index)
265+
.concat(embedToken,tokens.slice(index+1));
266+
moves.push({start: index,delta: embedToken.length-1});
267+
tokenInsertState.set(tokenRef,{
268+
nextIndex: index+embedToken.length,
269+
});
270+
}
219271
}
220272
}else{
221273
cached[raw]=tokens.concat();

0 commit comments

Comments
 (0)