Skip to content

Commit 1a6684c

Browse files
committed
feat: improve region regexes for snippet plugin
1 parent 1a2f81d commit 1a6684c

File tree

10 files changed

+72
-79
lines changed

10 files changed

+72
-79
lines changed

src/node/markdown/markdown.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ import type {
1919
ShikiTransformer,
2020
ThemeRegistrationAny
2121
} from '@shikijs/types'
22-
import type { Options } from 'markdown-it'
23-
import { MarkdownItAsync } from 'markdown-it-async'
2422
import anchorPlugin from 'markdown-it-anchor'
23+
import { MarkdownItAsync, type Options } from 'markdown-it-async'
2524
import attrsPlugin from 'markdown-it-attrs'
2625
import { full as emojiPlugin } from 'markdown-it-emoji'
2726
import type { BuiltinLanguage, BuiltinTheme, Highlighter } from 'shiki'

src/node/markdown/plugins/containers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type MarkdownIt from 'markdown-it'
1+
import type { MarkdownItAsync } from 'markdown-it-async'
22
import container from 'markdown-it-container'
33
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
44
import type Token from 'markdown-it/lib/token.mjs'
@@ -10,7 +10,7 @@ import {
1010
} from './preWrapper'
1111

1212
export const containerPlugin = (
13-
md: MarkdownIt,
13+
md: MarkdownItAsync,
1414
options: Options,
1515
containerOptions?: ContainerOptions
1616
) => {
@@ -54,7 +54,7 @@ type ContainerArgs = [typeof container, string, { render: RenderRule }]
5454
function createContainer(
5555
klass: string,
5656
defaultTitle: string,
57-
md: MarkdownIt
57+
md: MarkdownItAsync
5858
): ContainerArgs {
5959
return [
6060
container,
@@ -77,7 +77,7 @@ function createContainer(
7777
]
7878
}
7979

80-
function createCodeGroup(options: Options, md: MarkdownIt): ContainerArgs {
80+
function createCodeGroup(options: Options, md: MarkdownItAsync): ContainerArgs {
8181
return [
8282
container,
8383
'code-group',

src/node/markdown/plugins/githubAlerts.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type MarkdownIt from 'markdown-it'
1+
import type { MarkdownItAsync } from 'markdown-it-async'
22
import type { ContainerOptions } from './containers'
33

44
const markerRE =
55
/^\[!(TIP|NOTE|INFO|IMPORTANT|WARNING|CAUTION|DANGER)\]([^\n\r]*)/i
66

77
export const gitHubAlertsPlugin = (
8-
md: MarkdownIt,
8+
md: MarkdownItAsync,
99
options?: ContainerOptions
1010
) => {
1111
const titleMark = {

src/node/markdown/plugins/highlightLines.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// Now this plugin is only used to normalize line attrs.
33
// The else part of line highlights logic is in './highlight.ts'.
44

5-
import type MarkdownIt from 'markdown-it'
5+
import type { MarkdownItAsync } from 'markdown-it-async'
66

77
const RE = /{([\d,-]+)}/
88

9-
export const highlightLinePlugin = (md: MarkdownIt) => {
9+
export const highlightLinePlugin = (md: MarkdownItAsync) => {
1010
const fence = md.renderer.rules.fence!
1111
md.renderer.rules.fence = (...args) => {
1212
const [tokens, idx] = args

src/node/markdown/plugins/image.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// markdown-it plugin for normalizing image source
22

3-
import type MarkdownIt from 'markdown-it'
3+
import type { MarkdownItAsync } from 'markdown-it-async'
44
import { EXTERNAL_URL_RE } from '../../shared'
55

66
export interface Options {
@@ -11,7 +11,10 @@ export interface Options {
1111
lazyLoading?: boolean
1212
}
1313

14-
export const imagePlugin = (md: MarkdownIt, { lazyLoading }: Options = {}) => {
14+
export const imagePlugin = (
15+
md: MarkdownItAsync,
16+
{ lazyLoading }: Options = {}
17+
) => {
1518
const imageRule = md.renderer.rules.image!
1619
md.renderer.rules.image = (tokens, idx, options, env, self) => {
1720
const token = tokens[idx]

src/node/markdown/plugins/lineNumbers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// markdown-it plugin for generating line numbers.
22
// It depends on preWrapper plugin.
33

4-
import type MarkdownIt from 'markdown-it'
4+
import type { MarkdownItAsync } from 'markdown-it-async'
55

6-
export const lineNumberPlugin = (md: MarkdownIt, enable = false) => {
6+
export const lineNumberPlugin = (md: MarkdownItAsync, enable = false) => {
77
const fence = md.renderer.rules.fence!
88
md.renderer.rules.fence = (...args) => {
99
const rawCode = fence(...args)

src/node/markdown/plugins/link.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// 1. adding target="_blank" to external links
33
// 2. normalize internal links to end with `.html`
44

5-
import type MarkdownIt from 'markdown-it'
5+
import type { MarkdownItAsync } from 'markdown-it-async'
66
import { URL } from 'node:url'
77
import {
88
EXTERNAL_URL_RE,
@@ -14,7 +14,7 @@ import {
1414
const indexRE = /(^|.*\/)index.md(#?.*)$/i
1515

1616
export const linkPlugin = (
17-
md: MarkdownIt,
17+
md: MarkdownItAsync,
1818
externalAttrs: Record<string, string>,
1919
base: string
2020
) => {

src/node/markdown/plugins/preWrapper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type MarkdownIt from 'markdown-it'
1+
import type { MarkdownItAsync } from 'markdown-it-async'
22

33
export interface Options {
44
codeCopyButtonTitle: string
55
hasSingleTheme: boolean
66
}
77

8-
export function preWrapperPlugin(md: MarkdownIt, options: Options) {
8+
export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
99
const fence = md.renderer.rules.fence!
1010
md.renderer.rules.fence = (...args) => {
1111
const [tokens, idx] = args

src/node/markdown/plugins/restoreEntities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type MarkdownIt from 'markdown-it'
1+
import type { MarkdownItAsync } from 'markdown-it-async'
22
import type StateCore from 'markdown-it/lib/rules_core/state_core.mjs'
33
import type Token from 'markdown-it/lib/token.mjs'
44
import { escapeHtml } from '../../shared'
55

6-
export function restoreEntities(md: MarkdownIt): void {
6+
export function restoreEntities(md: MarkdownItAsync): void {
77
md.core.ruler.at('text_join', text_join)
88
md.renderer.rules.text = (tokens, idx) => escapeHtml(tokens[idx].content)
99
}

src/node/markdown/plugins/snippet.ts

Lines changed: 50 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import fs from 'fs-extra'
2-
import type MarkdownIt from 'markdown-it'
2+
import type { MarkdownItAsync } from 'markdown-it-async'
33
import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs'
44
import path from 'node:path'
55
import type { MarkdownEnv } from '../../shared'
@@ -51,78 +51,75 @@ export function dedent(text: string): string {
5151
return text
5252
}
5353

54+
const markers = [
55+
{
56+
start: /^\s*\/\/\s*#?region\b\s*(.*?)\s*$/,
57+
end: /^\s*\/\/\s*#?endregion\b\s*(.*?)\s*$/
58+
},
59+
{
60+
start: /^\s*<!--\s*#?region\b\s*(.*?)\s*-->/,
61+
end: /^\s*<!--\s*#?endregion\b\s*(.*?)\s*-->/
62+
},
63+
{
64+
start: /^\s*\/\*\s*#region\b\s*(.*?)\s*\*\//,
65+
end: /^\s*\/\*\s*#endregion\b\s*(.*?)\s*\*\//
66+
},
67+
{
68+
start: /^\s*#[rR]egion\b\s*(.*?)\s*$/,
69+
end: /^\s*#[eE]nd ?[rR]egion\b\s*(.*?)\s*$/
70+
},
71+
{
72+
start: /^\s*#\s*#?region\b\s*(.*?)\s*$/,
73+
end: /^\s*#\s*#?endregion\b\s*(.*?)\s*$/
74+
},
75+
{
76+
start: /^\s*(?:--|::|@?REM)\s*#region\b\s*(.*?)\s*$/,
77+
end: /^\s*(?:--|::|@?REM)\s*#endregion\b\s*(.*?)\s*$/
78+
},
79+
{
80+
start: /^\s*#pragma\s+region\b\s*(.*?)\s*$/,
81+
end: /^\s*#pragma\s+endregion\b\s*(.*?)\s*$/
82+
},
83+
{
84+
start: /^\s*\(\*\s*#region\b\s*(.*?)\s*\*\)/,
85+
end: /^\s*\(\*\s*#endregion\b\s*(.*?)\s*\*\)/
86+
}
87+
]
88+
5489
export function findRegion(lines: Array<string>, regionName: string) {
55-
const regionRegexps: [RegExp, RegExp][] = [
56-
[
57-
/^[ \t]*\/\/ ?#?(region) ([\w*-]+)$/,
58-
/^[ \t]*\/\/ ?#?(endregion) ?([\w*-]*)$/
59-
], // javascript, typescript, java
60-
[
61-
/^\/\* ?#(region) ([\w*-]+) ?\*\/$/,
62-
/^\/\* ?#(endregion) ?([\w*-]*) ?\*\/$/
63-
], // css, less, scss
64-
[/^#pragma (region) ([\w*-]+)$/, /^#pragma (endregion) ?([\w*-]*)$/], // C, C++
65-
[/^<!-- #?(region) ([\w*-]+) -->$/, /^<!-- #?(endregion) ?([\w*-]*) -->$/], // HTML, markdown
66-
[/^[ \t]*#(Region) ([\w*-]+)$/, /^[ \t]*#(End Region) ?([\w*-]*)$/], // Visual Basic
67-
[/^::#(region) ([\w*-]+)$/, /^::#(endregion) ?([\w*-]*)$/], // Bat
68-
[/^[ \t]*# ?(region) ([\w*-]+)$/, /^[ \t]*# ?(endregion) ?([\w*-]*)$/] // C#, PHP, Powershell, Python, perl & misc
69-
]
70-
71-
let chosenRegex: [RegExp, RegExp] | null = null
72-
let startLine = -1
90+
let chosen: { re: (typeof markers)[number]; start: number } | null = null
7391
// find the regex pair for a start marker that matches the given region name
7492
for (let i = 0; i < lines.length; i++) {
75-
const line = lines[i].trim()
76-
for (const [startRegex, endRegex] of regionRegexps) {
77-
const startMatch = startRegex.exec(line)
78-
if (
79-
startMatch &&
80-
startMatch[2] === regionName &&
81-
/^[rR]egion$/.test(startMatch[1])
82-
) {
83-
chosenRegex = [startRegex, endRegex]
84-
startLine = i + 1
93+
for (const re of markers) {
94+
if (re.start.exec(lines[i])?.[1] === regionName) {
95+
chosen = { re, start: i + 1 }
8596
break
8697
}
8798
}
88-
if (chosenRegex) break
99+
if (chosen) break
89100
}
90-
if (!chosenRegex) return null
101+
if (!chosen) return null
91102

92-
const [startRegex, endRegex] = chosenRegex
93103
let counter = 1
94104
// scan the rest of the lines to find the matching end marker, handling nested markers
95-
for (let i = startLine; i < lines.length; i++) {
96-
const trimmed = lines[i].trim()
105+
for (let i = chosen.start; i < lines.length; i++) {
97106
// check for an inner start marker for the same region
98-
const startMatch = startRegex.exec(trimmed)
99-
if (
100-
startMatch &&
101-
startMatch[2] === regionName &&
102-
/^[rR]egion$/.test(startMatch[1])
103-
) {
107+
if (chosen.re.start.exec(lines[i])?.[1] === regionName) {
104108
counter++
105109
continue
106110
}
107111
// check for an end marker for the same region
108-
const endMatch = endRegex.exec(trimmed)
109-
if (
110-
endMatch &&
111-
// allow empty region name on the end marker as a fallback
112-
(endMatch[2] === regionName || endMatch[2] === '') &&
113-
/^[Ee]nd ?[rR]egion$/.test(endMatch[1])
114-
) {
115-
counter--
116-
if (counter === 0) {
117-
return { start: startLine, end: i, regexp: chosenRegex }
118-
}
112+
const endRegion = chosen.re.end.exec(lines[i])?.[1]
113+
// allow empty region name on the end marker as a fallback
114+
if (endRegion === regionName || endRegion === '') {
115+
if (--counter === 0) return { ...chosen, end: i }
119116
}
120117
}
121118

122119
return null
123120
}
124121

125-
export const snippetPlugin = (md: MarkdownIt, srcDir: string) => {
122+
export const snippetPlugin = (md: MarkdownItAsync, srcDir: string) => {
126123
const parser: RuleBlock = (state, startLine, endLine, silent) => {
127124
const CH = '<'.charCodeAt(0)
128125
const pos = state.bMarks[startLine] + state.tShift[startLine]
@@ -205,13 +202,7 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => {
205202
content = dedent(
206203
lines
207204
.slice(region.start, region.end)
208-
.filter((line) => {
209-
const trimmed = line.trim()
210-
return (
211-
!region.regexp[0].test(trimmed) &&
212-
!region.regexp[1].test(trimmed)
213-
)
214-
})
205+
.filter((l) => !(region.re.start.test(l) || region.re.end.test(l)))
215206
.join('\n')
216207
)
217208
}

0 commit comments

Comments
 (0)