feat: support using titles instead of region names in markdown file inclusion

closes #4375
closes #4382

Co-authored-by: btea <2356281422@qq.com>
fix/4375
Divyansh Singh 7 months ago
parent 50db6aaa87
commit 36ef0a6088

@ -0,0 +1,27 @@
# header 1
header 1 content
## header 1.1
header 1.1 content
### header 1.1.1
header 1.1.1 content
### header 1.1.2
header 1.1.2 content
## header 1.2
header 1.2 content
### header 1.2.1
header 1.2.1 content
### header 1.2.2
header 1.2.2 content

@ -213,6 +213,10 @@ export default config
<!--@include: ./region-include.md#range-region{5,}--> <!--@include: ./region-include.md#range-region{5,}-->
## Markdown File Inclusion with Header
<!--@include: ./header-include.md#header-1-1-->
## Image Lazy Loading ## Image Lazy Loading
![vitepress logo](/vitepress.png) ![vitepress logo](/vitepress.png)

@ -897,6 +897,41 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co
Note that this does not throw errors if your file is not present. Hence, when using this feature make sure that the contents are being rendered as expected. Note that this does not throw errors if your file is not present. Hence, when using this feature make sure that the contents are being rendered as expected.
::: :::
Instead of VS Code regions, you can also use header anchors to include a specific section of the file. For example, if you have a header in your markdown file like this:
```md
## My Base Section
Some content here.
### My Sub Section
Some more content here.
## Another Section
Content outside `My Base Section`.
```
You can include the `My Base Section` section like this:
```md
## My Extended Section
<!--@include: ./parts/basics.md#my-base-section-->
```
**Equivalent code**
```md
## My Extended Section
Some content here.
### My Sub Section
Some more content here.
```
## Math Equations ## Math Equations
This is currently opt-in. To enable it, you need to install `markdown-it-mathjax3` and set `markdown.math` to `true` in your config file: This is currently opt-in. To enable it, you need to install `markdown-it-mathjax3` and set `markdown.math` to `true` in your config file:

@ -142,7 +142,7 @@ export async function createMarkdownToVueRenderFn(
// resolve includes // resolve includes
let includes: string[] = [] let includes: string[] = []
src = processIncludes(srcDir, src, fileOrig, includes) src = processIncludes(md, srcDir, src, fileOrig, includes)
const localeIndex = getLocaleForPath(siteConfig?.site, relativePath) const localeIndex = getLocaleForPath(siteConfig?.site, relativePath)

@ -56,7 +56,7 @@ export async function localSearchPlugin(
const relativePath = slash(path.relative(srcDir, file)) const relativePath = slash(path.relative(srcDir, file))
const env: MarkdownEnv = { path: file, relativePath, cleanUrls } const env: MarkdownEnv = { path: file, relativePath, cleanUrls }
const md_raw = await fs.promises.readFile(file, 'utf-8') const md_raw = await fs.promises.readFile(file, 'utf-8')
const md_src = processIncludes(srcDir, md_raw, file, []) const md_src = processIncludes(md, srcDir, md_raw, file, [])
if (options._render) { if (options._render) {
return await options._render(md_src, env, md) return await options._render(md_src, env, md)
} else { } else {

@ -1,18 +1,20 @@
import fs from 'fs-extra' import fs from 'fs-extra'
import matter from 'gray-matter' import matter from 'gray-matter'
import type { MarkdownItAsync } from 'markdown-it-async'
import path from 'node:path' import path from 'node:path'
import c from 'picocolors' import c from 'picocolors'
import { findRegion } from '../markdown/plugins/snippet' import { findRegion } from '../markdown/plugins/snippet'
import { slash } from '../shared' import { slash } from '../shared'
export function processIncludes( export function processIncludes(
md: MarkdownItAsync,
srcDir: string, srcDir: string,
src: string, src: string,
file: string, file: string,
includes: string[] includes: string[]
): string { ): string {
const includesRE = /<!--\s*@include:\s*(.*?)\s*-->/g const includesRE = /<!--\s*@include:\s*(.*?)\s*-->/g
const regionRE = /(#[\w-]+)/ const regionRE = /(#\S+)/
const rangeRE = /\{(\d*),(\d*)\}$/ const rangeRE = /\{(\d*),(\d*)\}$/
return src.replace(includesRE, (m: string, m1: string) => { return src.replace(includesRE, (m: string, m1: string) => {
@ -39,8 +41,30 @@ export function processIncludes(
if (region) { if (region) {
const [regionName] = region const [regionName] = region
const lines = content.split(/\r?\n/) const lines = content.split(/\r?\n/)
const regionLines = findRegion(lines, regionName.slice(1)) let { start, end } = findRegion(lines, regionName.slice(1)) ?? {}
content = lines.slice(regionLines?.start, regionLines?.end).join('\n')
if (start === undefined) {
// region not found, it might be a header
const tokens = md
.parse(content, {})
.filter((t) => t.type === 'heading_open' && t.map)
const idx = tokens.findIndex(
(t) => t.attrGet('id') === regionName.slice(1)
)
const token = tokens[idx]
if (token) {
start = token.map![1]
const level = parseInt(token.tag.slice(1))
for (let i = idx + 1; i < tokens.length; i++) {
if (parseInt(tokens[i].tag.slice(1)) <= level) {
end = tokens[i].map![0] - 1
break
}
}
}
}
content = lines.slice(start, end).join('\n')
} }
if (range) { if (range) {
@ -48,8 +72,8 @@ export function processIncludes(
const lines = content.split(/\r?\n/) const lines = content.split(/\r?\n/)
content = lines content = lines
.slice( .slice(
startLine ? parseInt(startLine, 10) - 1 : undefined, startLine ? parseInt(startLine) - 1 : undefined,
endLine ? parseInt(endLine, 10) : undefined endLine ? parseInt(endLine) : undefined
) )
.join('\n') .join('\n')
} }
@ -60,7 +84,7 @@ export function processIncludes(
includes.push(slash(includePath)) includes.push(slash(includePath))
// recursively process includes in the content // recursively process includes in the content
return processIncludes(srcDir, content, includePath, includes) return processIncludes(md, srcDir, content, includePath, includes)
// //
} catch (error) { } catch (error) {

Loading…
Cancel
Save