Skip to content

Commit

Permalink
endregion snippet extraction does not require tag
Browse files Browse the repository at this point in the history
The snippet extraction via region syntax from VSCode does not require the endregion to have a matching tag
  • Loading branch information
johnsimons committed Oct 15, 2024
1 parent fcae4d5 commit 4bf4b86
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 20 deletions.
143 changes: 142 additions & 1 deletion __tests__/unit/node/markdown/plugins/snippet.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { dedent, rawPathToToken } from 'node/markdown/plugins/snippet'
import {
dedent,
findRegion,
rawPathToToken
} from 'node/markdown/plugins/snippet'
import { expect } from 'vitest'

const removeEmptyKeys = <T extends Record<string, unknown>>(obj: T) => {
return Object.fromEntries(
Expand Down Expand Up @@ -99,4 +104,140 @@ describe('node/markdown/plugins/snippet', () => {
expect(removeEmptyKeys(rawPathToToken(rawPath))).toEqual(token)
})
})

describe('findRegion', () => {
test('when c# region with matching tag', () => {
const lines = `Console.WriteLine("Before region");
#region hello
Console.WriteLine("Hello, World!");
#endregion hello
Console.WriteLine("After region");`.split('\n')
const result = findRegion(lines, 'hello')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
'Console.WriteLine("Hello, World!");'
)
})
test('when c# region is not indented with spaces and no matching tag', () => {
const lines = `Console.WriteLine("Before region");
#region hello
Console.WriteLine("Hello, World!");
#endregion
Console.WriteLine("After region");`.split('\n')
const result = findRegion(lines, 'hello')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
'Console.WriteLine("Hello, World!");'
)
})
test('when c# region is indented with spaces and no matching tag', () => {
const lines = ` Console.WriteLine("Before region");
#region hello
Console.WriteLine("Hello, World!");
#endregion hello
Console.WriteLine("After region");`.split('\n')
const result = findRegion(lines, 'hello')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
' Console.WriteLine("Hello, World!");'
)
})
test('when c# region with matching tag', () => {
const lines = `Console.WriteLine("Before region");
#region hello
Console.WriteLine("Hello, World!");
#endregion hello
Console.WriteLine("After region");`.split('\n')
const result = findRegion(lines, 'hello')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
'Console.WriteLine("Hello, World!");'
)
})
test('when c# region is not indented with spaces and no matching tag', () => {
const lines = `Console.WriteLine("Before region");
#region hello
Console.WriteLine("Hello, World!");
#endregion
Console.WriteLine("After region");`.split('\n')
const result = findRegion(lines, 'hello')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
'Console.WriteLine("Hello, World!");'
)
})

test('when typescript region has matching tag', () => {
const lines = `let regexp: RegExp[] = []
// #region foo
let start = -1
// #endregion foo`.split('\n')
const result = findRegion(lines, 'foo')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
'let start = -1'
)
})
test('when typescript region is indented with spaces and no matching tag', () => {
const lines = ` let regexp: RegExp[] = []
// #region foo
let start = -1
// #endregion`.split('\n')
const result = findRegion(lines, 'foo')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
' let start = -1'
)
})

test('when css region has matching tag', () => {
const lines = `.body-content {
/* #region foo */
padding-left: 15px;
/* #endregion foo */
padding-right: 15px;
}`.split('\n')
const result = findRegion(lines, 'foo')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
' padding-left: 15px;'
)
})
test('when css region is indented with spaces and no matching tag', () => {
const lines = `.body-content {
/* #region foo */
padding-left: 15px;
/* #endregion */
padding-right: 15px;
}`.split('\n')
const result = findRegion(lines, 'foo')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
' padding-left: 15px;'
)
})

test('when html region has matching tag', () => {
const lines = `<!-- #region foo -->
<h1>Hello world</h1>
<!-- #endregion foo -->
<p>more text</p>`.split('\n')
const result = findRegion(lines, 'foo')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
' <h1>Hello world</h1>'
)
})
test('when html region is indented with spaces and no matching tag', () => {
const lines = ` <!-- #region foo -->
<h1>Hello world</h1>
<!-- #endregion foo -->
<p>more text</p>`.split('\n')
const result = findRegion(lines, 'foo')

expect(lines.slice(result?.start, result?.end).join('\n')).toBe(
' <h1>Hello world</h1>'
)
})
})
})
49 changes: 30 additions & 19 deletions src/node/markdown/plugins/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,38 +59,43 @@ function testLine(
) {
const [full, tag, name] = regexp.exec(line.trim()) || []

return (
full &&
tag &&
name === regionName &&
tag.match(end ? /^[Ee]nd ?[rR]egion$/ : /^[rR]egion$/)
)
return full && tag && end
? true
: name === regionName &&
tag.match(end ? /^[Ee]nd ?[rR]egion$/ : /^[rR]egion$/)
}

export function findRegion(lines: Array<string>, regionName: string) {
const regionRegexps = [
/^\/\/ ?#?((?:end)?region) ([\w*-]+)$/, // javascript, typescript, java
/^\/\* ?#((?:end)?region) ([\w*-]+) ?\*\/$/, // css, less, scss
/^#pragma ((?:end)?region) ([\w*-]+)$/, // C, C++
/^<!-- #?((?:end)?region) ([\w*-]+) -->$/, // HTML, markdown
/^#((?:End )Region) ([\w*-]+)$/, // Visual Basic
/^::#((?:end)region) ([\w*-]+)$/, // Bat
/^# ?((?:end)?region) ([\w*-]+)$/ // C#, PHP, Powershell, Python, perl & misc
[
/^[ \t]*\/\/ ?#?(region) ([\w*-]+)$/,
/^[ \t]*\/\/ ?#?(endregion) ?([\w*-]*)$/
], // javascript, typescript, java
[
/^\/\* ?#(region) ([\w*-]+) ?\*\/$/,
/^\/\* ?#(endregion) ?([\w*-]*) ?\*\/$/
], // css, less, scss
[/^#pragma (region) ([\w*-]+)$/, /^#pragma (endregion) ?([\w*-]*)$/], // C, C++
[/^<!-- #?(region) ([\w*-]+) -->$/, /^<!-- #?(endregion) ?([\w*-]*) -->$/], // HTML, markdown
[/^[ \t]*#(Region) ([\w*-]+)$/, /^[ \t]*#(End Region) ?([\w*-]*)$/], // Visual Basic
[/^::#(region) ([\w*-]+)$/, /^::#(endregion) ?([\w*-]*)$/], // Bat
[/^[ \t]*# ?(region) ([\w*-]+)$/, /^[ \t]*# ?(endregion) ?([\w*-]*)$/] // C#, PHP, Powershell, Python, perl & misc
]

let regexp = null
// #reqion foo
let regexp: RegExp[] = []
let start = -1

// #endregion
for (const [lineId, line] of lines.entries()) {
if (regexp === null) {
if (regexp.length === 0) {
for (const reg of regionRegexps) {
if (testLine(line, reg, regionName)) {
if (testLine(line, reg[0], regionName)) {
start = lineId + 1
regexp = reg
break
}
}
} else if (testLine(line, regexp, regionName, true)) {
} else if (testLine(line, regexp[1], regionName, true)) {
return { start, end: lineId, regexp }
}
}
Expand Down Expand Up @@ -181,7 +186,13 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => {
content = dedent(
lines
.slice(region.start, region.end)
.filter((line) => !region.regexp.test(line.trim()))
.filter((line) => {
const trimmed = line.trim()
return (
!region.regexp[0].test(trimmed) &&
!region.regexp[1].test(trimmed)
)
})
.join('\n')
)
}
Expand Down

0 comments on commit 4bf4b86

Please sign in to comment.