| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- 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(
- Object.entries(obj).filter(([, value]) => value !== '')
- ) as T
- }
- /* prettier-ignore */
- const rawPathTokenMap: [string, Partial<{ filepath: string, extension: string, title: string, region: string, lines: string, lang: string }>][] = [
- ['/path/to/file.extension', { filepath: '/path/to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['./path/to/file.extension', { filepath: './path/to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['/path to/file.extension', { filepath: '/path to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['./path to/file.extension', { filepath: './path to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['/path.to/file.extension', { filepath: '/path.to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['./path.to/file.extension', { filepath: './path.to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['/path .to/file.extension', { filepath: '/path .to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['./path .to/file.extension', { filepath: './path .to/file.extension', extension: 'extension', title: 'file.extension' }],
- ['/path/to/file', { filepath: '/path/to/file', title: 'file' }],
- ['./path/to/file', { filepath: './path/to/file', title: 'file' }],
- ['/path to/file', { filepath: '/path to/file', title: 'file' }],
- ['./path to/file', { filepath: './path to/file', title: 'file' }],
- ['/path.to/file', { filepath: '/path.to/file', title: 'file' }],
- ['./path.to/file', { filepath: './path.to/file', title: 'file' }],
- ['/path .to/file', { filepath: '/path .to/file', title: 'file' }],
- ['./path .to/file', { filepath: './path .to/file', title: 'file' }],
- ['/path/to/file.extension#region', { filepath: '/path/to/file.extension', extension: 'extension', title: 'file.extension', region: '#region' }],
- ['./path/to/file.extension {c#}', { filepath: './path/to/file.extension', extension: 'extension', title: 'file.extension', lang: 'c#' }],
- ['/path to/file.extension {1,2,4-6}', { filepath: '/path to/file.extension', extension: 'extension', title: 'file.extension', lines: '1,2,4-6' }],
- ['/path to/file.extension {1,2,4-6 c#}', { filepath: '/path to/file.extension', extension: 'extension', title: 'file.extension', lines: '1,2,4-6', lang: 'c#' }],
- ['/path.to/file.extension [title]', { filepath: '/path.to/file.extension', extension: 'extension', title: 'title' }],
- ['./path.to/file.extension#region {c#}', { filepath: './path.to/file.extension', extension: 'extension', title: 'file.extension', region: '#region', lang: 'c#' }],
- ['/path/to/file#region {1,2,4-6}', { filepath: '/path/to/file', title: 'file', region: '#region', lines: '1,2,4-6' }],
- ['./path/to/file#region {1,2,4-6 c#}', { filepath: './path/to/file', title: 'file', region: '#region', lines: '1,2,4-6', lang: 'c#' }],
- ['/path to/file {1,2,4-6 c#} [title]', { filepath: '/path to/file', title: 'title', lines: '1,2,4-6', lang: 'c#' }],
- ['./path to/file#region {1,2,4-6 c#} [title]', { filepath: './path to/file', title: 'title', region: '#region', lines: '1,2,4-6', lang: 'c#' }],
- ]
- describe('node/markdown/plugins/snippet', () => {
- describe('dedent', () => {
- test('when 0-level is minimal, do not remove spaces', () => {
- expect(
- dedent(
- [
- //
- 'fn main() {',
- ' println!("Hello");',
- '}'
- ].join('\n')
- )
- ).toMatchInlineSnapshot(`
- "fn main() {
- println!("Hello");
- }"
- `)
- })
- test('when 4-level is minimal, remove 4 spaces', () => {
- expect(
- dedent(
- [
- //
- ' let a = {',
- ' value: 42',
- ' };'
- ].join('\n')
- )
- ).toMatchInlineSnapshot(`
- "let a = {
- value: 42
- };"
- `)
- })
- test('when only 1 line is passed, dedent it', () => {
- expect(dedent(' let a = 42;')).toEqual('let a = 42;')
- })
- test('handle tabs as well', () => {
- expect(
- dedent(
- [
- //
- ' let a = {',
- ' value: 42',
- ' };'
- ].join('\n')
- )
- ).toMatchInlineSnapshot(`
- "let a = {
- value: 42
- };"
- `)
- })
- })
- describe('rawPathToToken', () => {
- test.each(rawPathTokenMap)('%s', (rawPath, token) => {
- expect(removeEmptyKeys(rawPathToToken(rawPath))).toEqual(token)
- })
- })
- describe('findRegion', () => {
- it('returns null when no region markers are present', () => {
- const lines = ['function foo() {', ' console.log("hello");', '}']
- expect(findRegion(lines, 'foo')).toBeNull()
- })
- it('ignores non-matching region names', () => {
- const lines = [
- '// #region regionA',
- 'some code here',
- '// #endregion regionA'
- ]
- expect(findRegion(lines, 'regionC')).toBeNull()
- })
- it('returns null if a region start marker exists without a matching end marker', () => {
- const lines = [
- '// #region missingEnd',
- 'console.log("inside region");',
- 'console.log("still inside");'
- ]
- expect(findRegion(lines, 'missingEnd')).toBeNull()
- })
- it('returns null if an end marker exists without a preceding start marker', () => {
- const lines = [
- '// #endregion ghostRegion',
- 'console.log("stray end marker");'
- ]
- expect(findRegion(lines, 'ghostRegion')).toBeNull()
- })
- it('detects C#/JavaScript style region markers with matching tags', () => {
- const lines = [
- 'Console.WriteLine("Before region");',
- '#region hello',
- 'Console.WriteLine("Hello, World!");',
- '#endregion hello',
- 'Console.WriteLine("After region");'
- ]
- const result = findRegion(lines, 'hello')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- 'Console.WriteLine("Hello, World!");'
- )
- }
- })
- it('detects region markers even when the end marker omits the region name', () => {
- const lines = [
- 'Console.WriteLine("Before region");',
- '#region hello',
- 'Console.WriteLine("Hello, World!");',
- '#endregion',
- 'Console.WriteLine("After region");'
- ]
- const result = findRegion(lines, 'hello')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- 'Console.WriteLine("Hello, World!");'
- )
- }
- })
- it('handles indented region markers correctly', () => {
- const lines = [
- ' Console.WriteLine("Before region");',
- ' #region hello',
- ' Console.WriteLine("Hello, World!");',
- ' #endregion hello',
- ' Console.WriteLine("After region");'
- ]
- const result = findRegion(lines, 'hello')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- ' Console.WriteLine("Hello, World!");'
- )
- }
- })
- it('detects TypeScript style region markers', () => {
- const lines = [
- 'let regexp: RegExp[] = [];',
- '// #region foo',
- 'let start = -1;',
- '// #endregion foo'
- ]
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- 'let start = -1;'
- )
- }
- })
- it('detects CSS style region markers', () => {
- const lines = [
- '.body-content {',
- '/* #region foo */',
- ' padding-left: 15px;',
- '/* #endregion foo */',
- ' padding-right: 15px;',
- '}'
- ]
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- ' padding-left: 15px;'
- )
- }
- })
- it('detects HTML style region markers', () => {
- const lines = [
- '<div>Some content</div>',
- '<!-- #region foo -->',
- ' <h1>Hello world</h1>',
- '<!-- #endregion foo -->',
- '<div>Other content</div>'
- ]
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- ' <h1>Hello world</h1>'
- )
- }
- })
- it('detects Visual Basic style region markers (with case-insensitive "End")', () => {
- const lines = [
- 'Console.WriteLine("VB")',
- '#Region VBRegion',
- ' Console.WriteLine("Inside region")',
- '#End Region VBRegion',
- 'Console.WriteLine("Done")'
- ]
- const result = findRegion(lines, 'VBRegion')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- ' Console.WriteLine("Inside region")'
- )
- }
- })
- it('detects Bat style region markers', () => {
- const lines = ['::#region foo', 'echo off', '::#endregion foo']
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- 'echo off'
- )
- }
- })
- it('detects C/C++ style region markers using #pragma', () => {
- const lines = [
- '#pragma region foo',
- 'int a = 1;',
- '#pragma endregion foo'
- ]
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- 'int a = 1;'
- )
- }
- })
- it('returns the first complete region when multiple regions exist', () => {
- const lines = [
- '// #region foo',
- 'first region content',
- '// #endregion foo',
- '// #region foo',
- 'second region content',
- '// #endregion foo'
- ]
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- expect(lines.slice(result.start, result.end).join('\n')).toBe(
- 'first region content'
- )
- }
- })
- it('handles nested regions with different names properly', () => {
- const lines = [
- '// #region foo',
- "console.log('line before nested');",
- '// #region bar',
- "console.log('nested content');",
- '// #endregion bar',
- '// #endregion foo'
- ]
- const result = findRegion(lines, 'foo')
- expect(result).not.toBeNull()
- if (result) {
- const extracted = lines.slice(result.start, result.end).join('\n')
- const expected = [
- "console.log('line before nested');",
- '// #region bar',
- "console.log('nested content');",
- '// #endregion bar'
- ].join('\n')
- expect(extracted).toBe(expected)
- }
- })
- })
- })
|