Skip to content

Commit 5d1dfc0

Browse files
committed
feat(markdown): include all regions with same name
VitePress supports including a part of a file through a VS Code region. However, when multiple regions have the same name, only one is included. The workaround plugin suggested in #3690 is no longer maintained. [^1] Furthermore, it suffers from a memory leak causing huge RAM usage. [^2] While we at Fabric Docs have forked the plugin [^3] to fix some issues, it still feels fragile. Another problem is having to use two different syntaxes (`<<<` vs `@[]()`) for including files, which caused confusion. Since this feature has been requested by VitePress directly, I don't think there's any point in trying to fix the plugin any further. fix #3690 [^1]: <fabioaanthony/markdown-it-vuepress-code-snippet-enhanced#7> [^2]: <https://discord.com/channels/507304429255393322/1208846408552030238/1407245292482330695> via <https://discord.gg/v6v4pMv> [^3]: <https://github.com/IMB11/md-it-enhanced-snippets>
1 parent be260fd commit 5d1dfc0

File tree

5 files changed

+234
-132
lines changed

5 files changed

+234
-132
lines changed

__tests__/unit/node/markdown/plugins/snippet.test.ts

Lines changed: 160 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
dedent,
3-
findRegion,
3+
findRegions,
44
rawPathToToken
55
} from 'node/markdown/plugins/snippet'
66
import { expect } from 'vitest'
@@ -106,9 +106,14 @@ describe('node/markdown/plugins/snippet', () => {
106106
})
107107

108108
describe('findRegion', () => {
109-
it('returns null when no region markers are present', () => {
110-
const lines = ['function foo() {', ' console.log("hello");', '}']
111-
expect(findRegion(lines, 'foo')).toBeNull()
109+
it('returns empty array when no region markers are present', () => {
110+
const lines = [
111+
'function foo() {',
112+
' console.log("hello");',
113+
' return "foo";',
114+
'}'
115+
]
116+
expect(findRegions(lines, 'foo')).toHaveLength(0)
112117
})
113118

114119
it('ignores non-matching region names', () => {
@@ -117,24 +122,24 @@ describe('node/markdown/plugins/snippet', () => {
117122
'some code here',
118123
'// #endregion regionA'
119124
]
120-
expect(findRegion(lines, 'regionC')).toBeNull()
125+
expect(findRegions(lines, 'regionC')).toHaveLength(0)
121126
})
122127

123-
it('returns null if a region start marker exists without a matching end marker', () => {
128+
it('returns empty array if a region start marker exists without a matching end marker', () => {
124129
const lines = [
125130
'// #region missingEnd',
126131
'console.log("inside region");',
127132
'console.log("still inside");'
128133
]
129-
expect(findRegion(lines, 'missingEnd')).toBeNull()
134+
expect(findRegions(lines, 'missingEnd')).toHaveLength(0)
130135
})
131136

132-
it('returns null if an end marker exists without a preceding start marker', () => {
137+
it('returns empty array if an end marker exists without a preceding start marker', () => {
133138
const lines = [
134139
'// #endregion ghostRegion',
135140
'console.log("stray end marker");'
136141
]
137-
expect(findRegion(lines, 'ghostRegion')).toBeNull()
142+
expect(findRegions(lines, 'ghostRegion')).toHaveLength(0)
138143
})
139144

140145
it('detects C#/JavaScript style region markers with matching tags', () => {
@@ -145,12 +150,18 @@ describe('node/markdown/plugins/snippet', () => {
145150
'#endregion hello',
146151
'Console.WriteLine("After region");'
147152
]
148-
const result = findRegion(lines, 'hello')
149-
expect(result).not.toBeNull()
153+
const result = findRegions(lines, 'hello')
154+
expect(result).toHaveLength(1)
150155
if (result) {
151-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
152-
'Console.WriteLine("Hello, World!");'
153-
)
156+
expect(
157+
result
158+
.flatMap((r) =>
159+
lines
160+
.slice(r.start, r.end)
161+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
162+
)
163+
.join('\n')
164+
).toBe('Console.WriteLine("Hello, World!");')
154165
}
155166
})
156167

@@ -162,12 +173,18 @@ describe('node/markdown/plugins/snippet', () => {
162173
'#endregion',
163174
'Console.WriteLine("After region");'
164175
]
165-
const result = findRegion(lines, 'hello')
166-
expect(result).not.toBeNull()
176+
const result = findRegions(lines, 'hello')
177+
expect(result).toHaveLength(1)
167178
if (result) {
168-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
169-
'Console.WriteLine("Hello, World!");'
170-
)
179+
expect(
180+
result
181+
.flatMap((r) =>
182+
lines
183+
.slice(r.start, r.end)
184+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
185+
)
186+
.join('\n')
187+
).toBe('Console.WriteLine("Hello, World!");')
171188
}
172189
})
173190

@@ -179,124 +196,182 @@ describe('node/markdown/plugins/snippet', () => {
179196
' #endregion hello',
180197
' Console.WriteLine("After region");'
181198
]
182-
const result = findRegion(lines, 'hello')
183-
expect(result).not.toBeNull()
199+
const result = findRegions(lines, 'hello')
200+
expect(result).toHaveLength(1)
184201
if (result) {
185-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
186-
' Console.WriteLine("Hello, World!");'
187-
)
202+
expect(
203+
result
204+
.flatMap((r) =>
205+
lines
206+
.slice(r.start, r.end)
207+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
208+
)
209+
.join('\n')
210+
).toBe(' Console.WriteLine("Hello, World!");')
188211
}
189212
})
190213

191214
it('detects TypeScript style region markers', () => {
192215
const lines = [
193216
'let regexp: RegExp[] = [];',
194-
'// #region foo',
217+
'// #region hello',
195218
'let start = -1;',
196-
'// #endregion foo'
219+
'// #endregion hello'
197220
]
198-
const result = findRegion(lines, 'foo')
199-
expect(result).not.toBeNull()
221+
const result = findRegions(lines, 'hello')
222+
expect(result).toHaveLength(1)
200223
if (result) {
201-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
202-
'let start = -1;'
203-
)
224+
expect(
225+
result
226+
.flatMap((r) =>
227+
lines
228+
.slice(r.start, r.end)
229+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
230+
)
231+
.join('\n')
232+
).toBe('let start = -1;')
204233
}
205234
})
206235

207236
it('detects CSS style region markers', () => {
208237
const lines = [
209238
'.body-content {',
210-
'/* #region foo */',
239+
'/* #region hello */',
211240
' padding-left: 15px;',
212-
'/* #endregion foo */',
241+
'/* #endregion hello */',
213242
' padding-right: 15px;',
214243
'}'
215244
]
216-
const result = findRegion(lines, 'foo')
217-
expect(result).not.toBeNull()
245+
const result = findRegions(lines, 'hello')
246+
expect(result).toHaveLength(1)
218247
if (result) {
219-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
220-
' padding-left: 15px;'
221-
)
248+
expect(
249+
result
250+
.flatMap((r) =>
251+
lines
252+
.slice(r.start, r.end)
253+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
254+
)
255+
.join('\n')
256+
).toBe(' padding-left: 15px;')
222257
}
223258
})
224259

225260
it('detects HTML style region markers', () => {
226261
const lines = [
227262
'<div>Some content</div>',
228-
'<!-- #region foo -->',
263+
'<!-- #region hello -->',
229264
' <h1>Hello world</h1>',
230-
'<!-- #endregion foo -->',
265+
'<!-- #endregion hello -->',
231266
'<div>Other content</div>'
232267
]
233-
const result = findRegion(lines, 'foo')
234-
expect(result).not.toBeNull()
268+
const result = findRegions(lines, 'hello')
269+
expect(result).toHaveLength(1)
235270
if (result) {
236-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
237-
' <h1>Hello world</h1>'
238-
)
271+
expect(
272+
result
273+
.flatMap((r) =>
274+
lines
275+
.slice(r.start, r.end)
276+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
277+
)
278+
.join('\n')
279+
).toBe(' <h1>Hello world</h1>')
239280
}
240281
})
241282

242283
it('detects Visual Basic style region markers (with case-insensitive "End")', () => {
243284
const lines = [
244285
'Console.WriteLine("VB")',
245-
'#Region VBRegion',
286+
'#Region hello',
246287
' Console.WriteLine("Inside region")',
247-
'#End Region VBRegion',
288+
'#End Region hello',
248289
'Console.WriteLine("Done")'
249290
]
250-
const result = findRegion(lines, 'VBRegion')
251-
expect(result).not.toBeNull()
291+
const result = findRegions(lines, 'hello')
292+
expect(result).toHaveLength(1)
252293
if (result) {
253-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
254-
' Console.WriteLine("Inside region")'
255-
)
294+
expect(
295+
result
296+
.flatMap((r) =>
297+
lines
298+
.slice(r.start, r.end)
299+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
300+
)
301+
.join('\n')
302+
).toBe(' Console.WriteLine("Inside region")')
256303
}
257304
})
258305

259306
it('detects Bat style region markers', () => {
260-
const lines = ['::#region foo', 'echo off', '::#endregion foo']
261-
const result = findRegion(lines, 'foo')
262-
expect(result).not.toBeNull()
307+
const lines = ['::#region hello', '@ECHO OFF', 'REM #endregion hello']
308+
const result = findRegions(lines, 'hello')
309+
expect(result).toHaveLength(1)
263310
if (result) {
264-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
265-
'echo off'
266-
)
311+
expect(
312+
result
313+
.flatMap((r) =>
314+
lines
315+
.slice(r.start, r.end)
316+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
317+
)
318+
.join('\n')
319+
).toBe('@ECHO OFF')
267320
}
268321
})
269322

270323
it('detects C/C++ style region markers using #pragma', () => {
271324
const lines = [
272-
'#pragma region foo',
325+
'#pragma region hello',
273326
'int a = 1;',
274-
'#pragma endregion foo'
327+
'#pragma endregion hello'
275328
]
276-
const result = findRegion(lines, 'foo')
277-
expect(result).not.toBeNull()
329+
const result = findRegions(lines, 'hello')
330+
expect(result).toHaveLength(1)
278331
if (result) {
279-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
280-
'int a = 1;'
281-
)
332+
expect(
333+
result
334+
.flatMap((r) =>
335+
lines
336+
.slice(r.start, r.end)
337+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
338+
)
339+
.join('\n')
340+
).toBe('int a = 1;')
282341
}
283342
})
284343

285-
it('returns the first complete region when multiple regions exist', () => {
344+
it('returns all regions with the same name when multiple exist', () => {
286345
const lines = [
287-
'// #region foo',
346+
'// #region hello',
288347
'first region content',
289-
'// #endregion foo',
290-
'// #region foo',
348+
'// #endregion hello',
349+
'between regions content',
350+
'// #region hello',
291351
'second region content',
292-
'// #endregion foo'
352+
'// #endregion',
353+
'between regions content',
354+
'// #region hello',
355+
'third region content',
356+
'// #endregion hello',
357+
'below regions content'
293358
]
294-
const result = findRegion(lines, 'foo')
295-
expect(result).not.toBeNull()
359+
const result = findRegions(lines, 'hello')
360+
expect(result).toHaveLength(3)
296361
if (result) {
297-
expect(lines.slice(result.start, result.end).join('\n')).toBe(
298-
'first region content'
299-
)
362+
const extracted = result
363+
.flatMap((r) =>
364+
lines
365+
.slice(r.start, r.end)
366+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
367+
)
368+
.join('\n')
369+
const expected = [
370+
'first region content',
371+
'second region content',
372+
'third region content'
373+
].join('\n')
374+
expect(extracted).toBe(expected)
300375
}
301376
})
302377

@@ -309,15 +384,19 @@ describe('node/markdown/plugins/snippet', () => {
309384
'// #endregion bar',
310385
'// #endregion foo'
311386
]
312-
const result = findRegion(lines, 'foo')
313-
expect(result).not.toBeNull()
387+
const result = findRegions(lines, 'foo')
388+
expect(result).toHaveLength(1)
314389
if (result) {
315-
const extracted = lines.slice(result.start, result.end).join('\n')
390+
const extracted = result
391+
.flatMap((r) =>
392+
lines
393+
.slice(r.start, r.end)
394+
.filter((l) => !(r.re.start.test(l) || r.re.end.test(l)))
395+
)
396+
.join('\n')
316397
const expected = [
317398
"console.log('line before nested');",
318-
'// #region bar',
319-
"console.log('nested content');",
320-
'// #endregion bar'
399+
"console.log('nested content');"
321400
].join('\n')
322401
expect(extracted).toBe(expected)
323402
}

0 commit comments

Comments
 (0)