react-codemirror-runmode 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/highlight.d.ts +2 -1
- package/dist/highlight.js +38 -6
- package/package.json +1 -1
- package/src/highlight.ts +62 -5
- package/test/index.spec.tsx +35 -3
package/dist/highlight.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Parser } from '@lezer/common';
|
|
2
2
|
import { Language, LanguageDescription } from '@codemirror/language';
|
|
3
3
|
import { Highlighter } from '@lezer/highlight';
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function getMarkdownParser(input: string, languages?: LanguageDescription[]): Promise<Parser>;
|
|
5
|
+
export declare function getCodeParser(input: string, languageName: string, fallbackLanguage?: Language, languages?: LanguageDescription[]): Promise<Parser | null>;
|
|
5
6
|
export declare function highlightCode<Output>(languageName: string, input: string, highlighter: Highlighter, fallbackLanguage: Language | undefined, languages: LanguageDescription[] | undefined, callback: (text: string, style: string | null, from: number, to: number) => Output): Promise<Output[]>;
|
package/dist/highlight.js
CHANGED
|
@@ -2,12 +2,44 @@ import { LanguageDescription } from '@codemirror/language';
|
|
|
2
2
|
import { highlightTree } from '@lezer/highlight';
|
|
3
3
|
import { languages as builtinLanguages } from '@codemirror/language-data';
|
|
4
4
|
import { markdown } from '@codemirror/lang-markdown';
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Extract language names from code blocks in markdown text
|
|
7
|
+
*/
|
|
8
|
+
function extractCodeBlockLanguages(text) {
|
|
9
|
+
const codeBlockRegex = /```(\w+)/g;
|
|
10
|
+
const languages = [];
|
|
11
|
+
let match;
|
|
12
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
13
|
+
languages.push(match[1]);
|
|
14
|
+
}
|
|
15
|
+
return languages;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pre-load parsers for a list of language names
|
|
19
|
+
*/
|
|
20
|
+
async function preloadLanguageParsers(languageNames, languages) {
|
|
21
|
+
return (await Promise.all(languageNames.map(async (langName) => {
|
|
22
|
+
const found = LanguageDescription.matchLanguageName(languages, langName, true);
|
|
23
|
+
if (found instanceof LanguageDescription) {
|
|
24
|
+
if (!found.support)
|
|
25
|
+
await found.load();
|
|
26
|
+
if (found.support) {
|
|
27
|
+
return found;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}))).filter((desc) => !!desc);
|
|
31
|
+
}
|
|
32
|
+
export async function getMarkdownParser(input, languages = builtinLanguages) {
|
|
33
|
+
const codeBlockLanguages = extractCodeBlockLanguages(input);
|
|
34
|
+
const preloadedLanguages = await preloadLanguageParsers(codeBlockLanguages || [], languages);
|
|
35
|
+
const langSupport = markdown({
|
|
36
|
+
codeLanguages: preloadedLanguages
|
|
37
|
+
});
|
|
38
|
+
return langSupport.language.parser;
|
|
39
|
+
}
|
|
40
|
+
export async function getCodeParser(input, languageName, fallbackLanguage, languages = builtinLanguages) {
|
|
6
41
|
if (languageName === 'markdown' || languageName === 'md') {
|
|
7
|
-
|
|
8
|
-
codeLanguages: builtinLanguages
|
|
9
|
-
});
|
|
10
|
-
return mdSupport.language.parser;
|
|
42
|
+
return await getMarkdownParser(input, languages);
|
|
11
43
|
}
|
|
12
44
|
else {
|
|
13
45
|
const found = LanguageDescription.matchLanguageName(languages, languageName, true);
|
|
@@ -22,7 +54,7 @@ export async function getCodeParser(languageName, fallbackLanguage, languages =
|
|
|
22
54
|
return fallbackLanguage ? fallbackLanguage.parser : null;
|
|
23
55
|
}
|
|
24
56
|
export async function highlightCode(languageName, input, highlighter, fallbackLanguage, languages, callback) {
|
|
25
|
-
const parser = await getCodeParser(languageName, fallbackLanguage, languages);
|
|
57
|
+
const parser = await getCodeParser(input, languageName, fallbackLanguage, languages);
|
|
26
58
|
if (parser) {
|
|
27
59
|
const tree = parser.parse(input);
|
|
28
60
|
const output = [];
|
package/package.json
CHANGED
package/src/highlight.ts
CHANGED
|
@@ -4,16 +4,68 @@ import { Highlighter, highlightTree } from '@lezer/highlight'
|
|
|
4
4
|
import { languages as builtinLanguages } from '@codemirror/language-data'
|
|
5
5
|
import { markdown } from '@codemirror/lang-markdown'
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Extract language names from code blocks in markdown text
|
|
9
|
+
*/
|
|
10
|
+
function extractCodeBlockLanguages(text: string): string[] {
|
|
11
|
+
const codeBlockRegex = /```(\w+)/g
|
|
12
|
+
const languages: string[] = []
|
|
13
|
+
let match
|
|
14
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
15
|
+
languages.push(match[1])
|
|
16
|
+
}
|
|
17
|
+
return languages
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Pre-load parsers for a list of language names
|
|
22
|
+
*/
|
|
23
|
+
async function preloadLanguageParsers(
|
|
24
|
+
languageNames: string[],
|
|
25
|
+
languages: LanguageDescription[]
|
|
26
|
+
): Promise<LanguageDescription[]> {
|
|
27
|
+
return (
|
|
28
|
+
await Promise.all(
|
|
29
|
+
languageNames.map(async langName => {
|
|
30
|
+
const found = LanguageDescription.matchLanguageName(
|
|
31
|
+
languages,
|
|
32
|
+
langName,
|
|
33
|
+
true
|
|
34
|
+
)
|
|
35
|
+
if (found instanceof LanguageDescription) {
|
|
36
|
+
if (!found.support) await found.load()
|
|
37
|
+
if (found.support) {
|
|
38
|
+
return found
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
)
|
|
43
|
+
).filter((desc): desc is LanguageDescription => !!desc)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function getMarkdownParser(
|
|
47
|
+
input: string,
|
|
48
|
+
languages: LanguageDescription[] = builtinLanguages
|
|
49
|
+
): Promise<Parser> {
|
|
50
|
+
const codeBlockLanguages = extractCodeBlockLanguages(input)
|
|
51
|
+
const preloadedLanguages = await preloadLanguageParsers(
|
|
52
|
+
codeBlockLanguages || [],
|
|
53
|
+
languages
|
|
54
|
+
)
|
|
55
|
+
const langSupport = markdown({
|
|
56
|
+
codeLanguages: preloadedLanguages
|
|
57
|
+
})
|
|
58
|
+
return langSupport.language.parser
|
|
59
|
+
}
|
|
60
|
+
|
|
7
61
|
export async function getCodeParser(
|
|
62
|
+
input: string,
|
|
8
63
|
languageName: string,
|
|
9
64
|
fallbackLanguage?: Language,
|
|
10
65
|
languages: LanguageDescription[] = builtinLanguages
|
|
11
66
|
): Promise<Parser | null> {
|
|
12
67
|
if (languageName === 'markdown' || languageName === 'md') {
|
|
13
|
-
|
|
14
|
-
codeLanguages: builtinLanguages
|
|
15
|
-
})
|
|
16
|
-
return mdSupport.language.parser
|
|
68
|
+
return await getMarkdownParser(input, languages)
|
|
17
69
|
} else {
|
|
18
70
|
const found = LanguageDescription.matchLanguageName(
|
|
19
71
|
languages,
|
|
@@ -41,7 +93,12 @@ export async function highlightCode<Output>(
|
|
|
41
93
|
to: number
|
|
42
94
|
) => Output
|
|
43
95
|
): Promise<Output[]> {
|
|
44
|
-
const parser = await getCodeParser(
|
|
96
|
+
const parser = await getCodeParser(
|
|
97
|
+
input,
|
|
98
|
+
languageName,
|
|
99
|
+
fallbackLanguage,
|
|
100
|
+
languages
|
|
101
|
+
)
|
|
45
102
|
if (parser) {
|
|
46
103
|
const tree = parser.parse(input)
|
|
47
104
|
const output: Array<Output> = []
|
package/test/index.spec.tsx
CHANGED
|
@@ -10,15 +10,15 @@ const sleep = (msec: number) =>
|
|
|
10
10
|
|
|
11
11
|
describe('getCodeParser', () => {
|
|
12
12
|
it('loads a JavaScript parser', async () => {
|
|
13
|
-
const parser = await getCodeParser('js')
|
|
13
|
+
const parser = await getCodeParser('', 'js')
|
|
14
14
|
expect(parser).toBeInstanceOf(Parser)
|
|
15
15
|
})
|
|
16
16
|
it('loads a Python parser', async () => {
|
|
17
|
-
const parser = await getCodeParser('python')
|
|
17
|
+
const parser = await getCodeParser('', 'python')
|
|
18
18
|
expect(parser).toBeInstanceOf(Parser)
|
|
19
19
|
})
|
|
20
20
|
it('returns null for non-existing languages', async () => {
|
|
21
|
-
const parser = await getCodeParser('foobar123')
|
|
21
|
+
const parser = await getCodeParser('', 'foobar123')
|
|
22
22
|
expect(parser).toBeNull()
|
|
23
23
|
})
|
|
24
24
|
})
|
|
@@ -99,6 +99,38 @@ describe('Highlight codeblocks', () => {
|
|
|
99
99
|
expect(numberToken).toBeDefined()
|
|
100
100
|
expect(numberToken?.style).not.toBeNull()
|
|
101
101
|
})
|
|
102
|
+
|
|
103
|
+
it('highlights typescript codeblocks in markdown', async () => {
|
|
104
|
+
const mdCode = '```typescript\nconst x: number = 123\n```'
|
|
105
|
+
const highlighted = await highlightCode(
|
|
106
|
+
'markdown',
|
|
107
|
+
mdCode,
|
|
108
|
+
oneDarkHighlightStyle,
|
|
109
|
+
undefined,
|
|
110
|
+
undefined,
|
|
111
|
+
(text, style, from, to) => ({ text, style, from, to })
|
|
112
|
+
)
|
|
113
|
+
expect(highlighted.length).toBeGreaterThan(0)
|
|
114
|
+
|
|
115
|
+
// Find the language info token
|
|
116
|
+
const langInfo = highlighted.find(t => t.text === 'typescript')
|
|
117
|
+
expect(langInfo).toBeDefined()
|
|
118
|
+
|
|
119
|
+
// Find the TS keyword 'const'
|
|
120
|
+
const constToken = highlighted.find(t => t.text === 'const')
|
|
121
|
+
expect(constToken).toBeDefined()
|
|
122
|
+
expect(constToken?.style).not.toBeNull()
|
|
123
|
+
|
|
124
|
+
// Find the type annotation 'number'
|
|
125
|
+
const typeToken = highlighted.find(t => t.text === 'number')
|
|
126
|
+
expect(typeToken).toBeDefined()
|
|
127
|
+
expect(typeToken?.style).not.toBeNull()
|
|
128
|
+
|
|
129
|
+
// Find the number '123'
|
|
130
|
+
const numberToken = highlighted.find(t => t.text === '123')
|
|
131
|
+
expect(numberToken).toBeDefined()
|
|
132
|
+
expect(numberToken?.style).not.toBeNull()
|
|
133
|
+
})
|
|
102
134
|
})
|
|
103
135
|
|
|
104
136
|
describe('React Highlighter', () => {
|