1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Chore: Mobile: Fix CodeMirror test failures (#7522)

This commit is contained in:
Henry Heino 2022-12-30 09:25:31 -08:00 committed by GitHub
parent 767213cdc1
commit e7386e6fe3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 293 additions and 252 deletions

View File

@ -933,6 +933,9 @@ packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/createEditor.js.ma
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.d.ts packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.d.ts
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js.map packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js.map
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/loadLanguages.d.ts
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/loadLanguages.js
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/loadLanguages.js.map
packages/app-mobile/components/NoteEditor/CodeMirror/theme.d.ts packages/app-mobile/components/NoteEditor/CodeMirror/theme.d.ts
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js packages/app-mobile/components/NoteEditor/CodeMirror/theme.js
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js.map packages/app-mobile/components/NoteEditor/CodeMirror/theme.js.map

3
.gitignore vendored
View File

@ -921,6 +921,9 @@ packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/createEditor.js.ma
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.d.ts packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.d.ts
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js.map packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js.map
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/loadLanguages.d.ts
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/loadLanguages.js
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/loadLanguages.js.map
packages/app-mobile/components/NoteEditor/CodeMirror/theme.d.ts packages/app-mobile/components/NoteEditor/CodeMirror/theme.d.ts
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js packages/app-mobile/components/NoteEditor/CodeMirror/theme.js
packages/app-mobile/components/NoteEditor/CodeMirror/theme.js.map packages/app-mobile/components/NoteEditor/CodeMirror/theme.js.map

View File

@ -7,6 +7,7 @@ import { initCodeMirror } from './CodeMirror';
import { themeStyle } from '@joplin/lib/theme'; import { themeStyle } from '@joplin/lib/theme';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import { forceParsing } from '@codemirror/language'; import { forceParsing } from '@codemirror/language';
import loadLangauges from './testUtil/loadLanguages';
const createEditorSettings = (themeId: number) => { const createEditorSettings = (themeId: number) => {
@ -27,6 +28,7 @@ describe('CodeMirror', () => {
const initialText = `${headerLineText}\nThis is a test.`; const initialText = `${headerLineText}\nThis is a test.`;
const editorSettings = createEditorSettings(Setting.THEME_LIGHT); const editorSettings = createEditorSettings(Setting.THEME_LIGHT);
await loadLangauges();
const editor = initCodeMirror(document.body, initialText, editorSettings); const editor = initCodeMirror(document.body, initialText, editorSettings);
// Force the generation of the syntax tree now. // Force the generation of the syntax tree now.

View File

@ -10,10 +10,11 @@ describe('markdownCommands.bulletedVsChecklist', () => {
const bulletedListPart = '- Test\n- This is a test.\n- 3\n- 4\n- 5'; const bulletedListPart = '- Test\n- This is a test.\n- 3\n- 4\n- 5';
const checklistPart = '- [ ] This is a checklist\n- [ ] with multiple items.\n- [ ] ☑'; const checklistPart = '- [ ] This is a checklist\n- [ ] with multiple items.\n- [ ] ☑';
const initialDocText = `${bulletedListPart}\n\n${checklistPart}`; const initialDocText = `${bulletedListPart}\n\n${checklistPart}`;
const expectedTags = ['BulletList', 'Task'];
it('should remove a checklist following a bulleted list without modifying the bulleted list', () => { it('should remove a checklist following a bulleted list without modifying the bulleted list', async () => {
const editor = createEditor( const editor = await createEditor(
initialDocText, EditorSelection.cursor(bulletedListPart.length + 5) initialDocText, EditorSelection.cursor(bulletedListPart.length + 5), expectedTags
); );
toggleList(ListType.CheckList)(editor); toggleList(ListType.CheckList)(editor);
@ -22,9 +23,9 @@ describe('markdownCommands.bulletedVsChecklist', () => {
); );
}); });
it('should remove an unordered list following a checklist without modifying the checklist', () => { it('should remove an unordered list following a checklist without modifying the checklist', async () => {
const editor = createEditor( const editor = await createEditor(
initialDocText, EditorSelection.cursor(bulletedListPart.length - 5) initialDocText, EditorSelection.cursor(bulletedListPart.length - 5), expectedTags
); );
toggleList(ListType.UnorderedList)(editor); toggleList(ListType.UnorderedList)(editor);
@ -33,9 +34,9 @@ describe('markdownCommands.bulletedVsChecklist', () => {
); );
}); });
it('should replace a selection of unordered and task lists with a correctly-numbered list', () => { it('should replace a selection of unordered and task lists with a correctly-numbered list', async () => {
const editor = createEditor( const editor = await createEditor(
initialDocText, EditorSelection.range(0, initialDocText.length) initialDocText, EditorSelection.range(0, initialDocText.length), expectedTags
); );
toggleList(ListType.OrderedList)(editor); toggleList(ListType.OrderedList)(editor);

View File

@ -2,36 +2,18 @@
* @jest-environment jsdom * @jest-environment jsdom
*/ */
import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state'; import { EditorSelection } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { import {
toggleBolded, toggleCode, toggleHeaderLevel, toggleItalicized, toggleMath, updateLink, toggleBolded, toggleCode, toggleHeaderLevel, toggleItalicized, toggleMath, updateLink,
} from './markdownCommands'; } from './markdownCommands';
import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown'; import createEditor from './testUtil/createEditor';
import { markdown } from '@codemirror/lang-markdown'; import { blockMathTagName } from './markdownMathParser';
import { MarkdownMathExtension } from './markdownMathParser';
import { indentUnit } from '@codemirror/language';
// Creates and returns a minimal editor with markdown extensions
const createEditor = (initialText: string, initialSelection: SelectionRange): EditorView => {
return new EditorView({
doc: initialText,
selection: EditorSelection.create([initialSelection]),
extensions: [
markdown({
extensions: [MarkdownMathExtension, GithubFlavoredMarkdownExt],
}),
indentUnit.of('\t'),
EditorState.tabSize.of(4),
],
});
};
describe('markdownCommands', () => { describe('markdownCommands', () => {
it('should bold/italicize everything selected', () => { it('should bold/italicize everything selected', async () => {
const initialDocText = 'Testing...'; const initialDocText = 'Testing...';
const editor = createEditor( const editor = await createEditor(
initialDocText, EditorSelection.range(0, initialDocText.length) initialDocText, EditorSelection.range(0, initialDocText.length), []
); );
toggleBolded(editor); toggleBolded(editor);
@ -55,10 +37,10 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('Testing...'); expect(editor.state.doc.toString()).toBe('Testing...');
}); });
it('for a cursor, bolding, then italicizing, should produce a bold-italic region', () => { it('for a cursor, bolding, then italicizing, should produce a bold-italic region', async () => {
const initialDocText = ''; const initialDocText = '';
const editor = createEditor( const editor = await createEditor(
initialDocText, EditorSelection.cursor(0) initialDocText, EditorSelection.cursor(0), []
); );
toggleBolded(editor); toggleBolded(editor);
@ -73,9 +55,9 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('***Test*** Test'); expect(editor.state.doc.toString()).toBe('***Test*** Test');
}); });
it('toggling math should both create and navigate out of math regions', () => { it('toggling math should both create and navigate out of math regions', async () => {
const initialDocText = 'Testing... '; const initialDocText = 'Testing... ';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), []);
toggleMath(editor); toggleMath(editor);
expect(editor.state.doc.toString()).toBe('Testing... $$'); expect(editor.state.doc.toString()).toBe('Testing... $$');
@ -89,9 +71,9 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('Testing... $3 + 3 \\neq 5$...'); expect(editor.state.doc.toString()).toBe('Testing... $3 + 3 \\neq 5$...');
}); });
it('toggling inline code should both create and navigate out of an inline code region', () => { it('toggling inline code should both create and navigate out of an inline code region', async () => {
const initialDocText = 'Testing...\n\n'; const initialDocText = 'Testing...\n\n';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), []);
toggleCode(editor); toggleCode(editor);
editor.dispatch(editor.state.replaceSelection('f(x) = ...')); editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
@ -101,9 +83,9 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('Testing...\n\n`f(x) = ...` is a function.'); expect(editor.state.doc.toString()).toBe('Testing...\n\n`f(x) = ...` is a function.');
}); });
it('should set headers to the proper levels (when toggling)', () => { it('should set headers to the proper levels (when toggling)', async () => {
const initialDocText = 'Testing...\nThis is a test.'; const initialDocText = 'Testing...\nThis is a test.';
const editor = createEditor(initialDocText, EditorSelection.cursor(3)); const editor = await createEditor(initialDocText, EditorSelection.cursor(3), []);
toggleHeaderLevel(1)(editor); toggleHeaderLevel(1)(editor);
@ -127,11 +109,12 @@ describe('markdownCommands', () => {
expect(mainSel.from).toBe('Testing...'.length); expect(mainSel.from).toBe('Testing...'.length);
}); });
it('headers should toggle properly within block quotes', () => { it('headers should toggle properly within block quotes', async () => {
const initialDocText = 'Testing...\n\n> This is a test.\n> ...a test'; const initialDocText = 'Testing...\n\n> This is a test.\n> ...a test';
const editor = createEditor( const editor = await createEditor(
initialDocText, initialDocText,
EditorSelection.cursor('Testing...\n\n> This'.length) EditorSelection.cursor('Testing...\n\n> This'.length),
['Blockquote']
); );
toggleHeaderLevel(1)(editor); toggleHeaderLevel(1)(editor);
@ -150,69 +133,48 @@ describe('markdownCommands', () => {
); );
}); });
// We need to disable this test because it randomly fails on CI. it('block math should be created correctly within block quotes', async () => {
// const initialDocText = 'Testing...\n\n> This is a test.\n> y = mx + b\n> ...a test';
// ● markdownCommands › block math should properly toggle within block quotes const editor = await createEditor(
// initialDocText,
// expect(received).toEqual(expected) // deep equality EditorSelection.range(
// 'Testing...\n\n> This'.length,
// - Expected - 1 'Testing...\n\n> This is a test.\n> y = mx + b'.length
// + Received + 3 ),
// ['Blockquote']
// Testing... );
//
// - > This is a test.
// + > $$
// + > This is$$ a test.
// > y = mx + b
// + > $$
// > ...a test
//
// 179 | toggleMath(editor);
// 180 | mainSel = editor.state.selection.main;
// > 181 | expect(editor.state.doc.toString()).toEqual(initialDocText);
// | ^
// 182 | expect(mainSel.from).toBe('Testing...\n\n'.length);
// 183 | expect(mainSel.to).toBe('Testing...\n\n> This is a test.\n> y = mx + b'.length);
// 184 | });
toggleMath(editor);
// it('block math should properly toggle within block quotes', () => { // Toggling math should surround the content in '$$'s
// const initialDocText = 'Testing...\n\n> This is a test.\n> y = mx + b\n> ...a test'; const mainSel = editor.state.selection.main;
// const editor = createEditor( expect(editor.state.doc.toString()).toEqual(
// initialDocText, 'Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$\n> ...a test'
// EditorSelection.range( );
// 'Testing...\n\n> This'.length, expect(mainSel.from).toBe('Testing...\n\n'.length);
// 'Testing...\n\n> This is a test.\n> y = mx + b'.length expect(mainSel.to).toBe('Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$'.length);
// ) });
// );
// toggleMath(editor); it('block math should be correctly removed within block quotes', async () => {
const initialDocText = 'Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$\n> ...a test';
// // Toggling math should surround the content in '$$'s const editor = await createEditor(
// let mainSel = editor.state.selection.main; initialDocText,
// expect(editor.state.doc.toString()).toEqual( EditorSelection.cursor('Testing...\n\n> $$\n> This is'.length),
// 'Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$\n> ...a test' ['Blockquote', blockMathTagName]
// ); );
// expect(mainSel.from).toBe('Testing...\n\n'.length);
// expect(mainSel.to).toBe('Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$'.length);
// // Change to a cursor --- test cursor expansion // Toggling math should remove the '$$'s
// editor.dispatch({ toggleMath(editor);
// selection: EditorSelection.cursor('Testing...\n\n> $$\n> This is'.length), const mainSel = editor.state.selection.main;
// }); expect(editor.state.doc.toString()).toEqual('Testing...\n\n> This is a test.\n> y = mx + b\n> ...a test');
expect(mainSel.from).toBe('Testing...\n\n'.length);
expect(mainSel.to).toBe('Testing...\n\n> This is a test.\n> y = mx + b'.length);
});
// // Toggling math again should remove the '$$'s it('updateLink should replace link titles and isolate URLs if no title is given', async () => {
// toggleMath(editor);
// mainSel = editor.state.selection.main;
// expect(editor.state.doc.toString()).toEqual(initialDocText);
// expect(mainSel.from).toBe('Testing...\n\n'.length);
// expect(mainSel.to).toBe('Testing...\n\n> This is a test.\n> y = mx + b'.length);
// });
it('updateLink should replace link titles and isolate URLs if no title is given', () => {
const initialDocText = '[foo](http://example.com/)'; const initialDocText = '[foo](http://example.com/)';
const editor = createEditor(initialDocText, EditorSelection.cursor('[f'.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor('[f'.length), ['Link']);
updateLink('bar', 'https://example.com/')(editor); updateLink('bar', 'https://example.com/')(editor);
expect(editor.state.doc.toString()).toBe( expect(editor.state.doc.toString()).toBe(
@ -225,9 +187,9 @@ describe('markdownCommands', () => {
); );
}); });
it('toggling math twice, starting on a line with content, should a math block', () => { it('toggling math twice, starting on a line with content, should a math block', async () => {
const initialDocText = 'Testing... '; const initialDocText = 'Testing... ';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), []);
toggleMath(editor); toggleMath(editor);
toggleMath(editor); toggleMath(editor);
@ -235,9 +197,9 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('Testing... \n$$\nf(x) = ...\n$$'); expect(editor.state.doc.toString()).toBe('Testing... \n$$\nf(x) = ...\n$$');
}); });
it('toggling math twice on an empty line should create an empty math block', () => { it('toggling math twice on an empty line should create an empty math block', async () => {
const initialDocText = 'Testing...\n\n'; const initialDocText = 'Testing...\n\n';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), []);
toggleMath(editor); toggleMath(editor);
toggleMath(editor); toggleMath(editor);
@ -245,9 +207,9 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('Testing...\n\n$$\nf(x) = ...\n$$'); expect(editor.state.doc.toString()).toBe('Testing...\n\n$$\nf(x) = ...\n$$');
}); });
it('toggling code twice on an empty line should create an empty code block', () => { it('toggling code twice on an empty line should create an empty code block', async () => {
const initialDocText = 'Testing...\n\n'; const initialDocText = 'Testing...\n\n';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), []);
// Toggling code twice should create a block code region // Toggling code twice should create a block code region
toggleCode(editor); toggleCode(editor);
@ -259,9 +221,9 @@ describe('markdownCommands', () => {
expect(editor.state.doc.toString()).toBe('Testing...\n\nf(x) = ...\n'); expect(editor.state.doc.toString()).toBe('Testing...\n\nf(x) = ...\n');
}); });
it('toggling math twice inside a block quote should produce an empty math block', () => { it('toggling math twice inside a block quote should produce an empty math block', async () => {
const initialDocText = '> Testing...> \n> '; const initialDocText = '> Testing...> \n> ';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), ['Blockquote']);
toggleMath(editor); toggleMath(editor);
toggleMath(editor); toggleMath(editor);
@ -278,9 +240,9 @@ describe('markdownCommands', () => {
expect(sel.to).toBe(editor.state.doc.length); expect(sel.to).toBe(editor.state.doc.length);
}); });
it('toggling inline code should both create and navigate out of an inline code region', () => { it('toggling inline code should both create and navigate out of an inline code region', async () => {
const initialDocText = 'Testing...\n\n'; const initialDocText = 'Testing...\n\n';
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length)); const editor = await createEditor(initialDocText, EditorSelection.cursor(initialDocText.length), []);
toggleCode(editor); toggleCode(editor);
editor.dispatch(editor.state.replaceSelection('f(x) = ...')); editor.dispatch(editor.state.replaceSelection('f(x) = ...'));

View File

@ -10,25 +10,27 @@ import { ListType } from '../types';
import createEditor from './testUtil/createEditor'; import createEditor from './testUtil/createEditor';
describe('markdownCommands.toggleList', () => { describe('markdownCommands.toggleList', () => {
it('should remove the same type of list', () => { it('should remove the same type of list', async () => {
const initialDocText = '- testing\n- this is a test'; const initialDocText = '- testing\n- this is a `test`\n';
const editor = createEditor( const editor = await createEditor(
initialDocText, initialDocText,
EditorSelection.cursor(5) EditorSelection.cursor(5),
['BulletList', 'InlineCode']
); );
toggleList(ListType.UnorderedList)(editor); toggleList(ListType.UnorderedList)(editor);
expect(editor.state.doc.toString()).toBe( expect(editor.state.doc.toString()).toBe(
'testing\nthis is a test' 'testing\nthis is a `test`\n'
); );
}); });
it('should insert a numbered list with correct numbering', () => { it('should insert a numbered list with correct numbering', async () => {
const initialDocText = 'Testing...\nThis is a test\nof list toggling...'; const initialDocText = 'Testing...\nThis is a test\nof list toggling...';
const editor = createEditor( const editor = await createEditor(
initialDocText, initialDocText,
EditorSelection.cursor('Testing...\nThis is a'.length) EditorSelection.cursor('Testing...\nThis is a'.length),
[]
); );
toggleList(ListType.OrderedList)(editor); toggleList(ListType.OrderedList)(editor);
@ -47,12 +49,13 @@ describe('markdownCommands.toggleList', () => {
); );
}); });
const numberedListText = '- 1\n- 2\n- 3\n- 4\n- 5\n- 6\n- 7'; const unorderedListText = '- 1\n- 2\n- 3\n- 4\n- 5\n- 6\n- 7';
it('should correctly replace an unordered list with a numbered list', () => { it('should correctly replace an unordered list with a numbered list', async () => {
const editor = createEditor( const editor = await createEditor(
numberedListText, unorderedListText,
EditorSelection.cursor(numberedListText.length) EditorSelection.cursor(unorderedListText.length),
['BulletList']
); );
toggleList(ListType.OrderedList)(editor); toggleList(ListType.OrderedList)(editor);
@ -62,10 +65,11 @@ describe('markdownCommands.toggleList', () => {
}); });
it('should correctly replace an unordered list with a checklist', () => { it('should correctly replace an unordered list with a checklist', async () => {
const editor = createEditor( const editor = await createEditor(
numberedListText, unorderedListText,
EditorSelection.cursor(numberedListText.length) EditorSelection.cursor(unorderedListText.length),
['BulletList']
); );
toggleList(ListType.CheckList)(editor); toggleList(ListType.CheckList)(editor);
@ -74,13 +78,14 @@ describe('markdownCommands.toggleList', () => {
); );
}); });
it('should properly toggle a sublist of a bulleted list', () => { it('should properly toggle a sublist of a bulleted list', async () => {
const preSubListText = '# List test\n * This\n * is\n'; const preSubListText = '# List test\n * This\n * is\n';
const initialDocText = `${preSubListText}\t* a\n\t* test\n * of list toggling`; const initialDocText = `${preSubListText}\t* a\n\t* test\n * of list toggling`;
const editor = createEditor( const editor = await createEditor(
initialDocText, initialDocText,
EditorSelection.cursor(preSubListText.length + '\t* a'.length) EditorSelection.cursor(preSubListText.length + '\t* a'.length),
['BulletList', 'ATXHeading1']
); );
// Indentation should be preserved when changing list types // Indentation should be preserved when changing list types
@ -94,6 +99,17 @@ describe('markdownCommands.toggleList', () => {
expect(editor.state.selection.main.to).toBe( expect(editor.state.selection.main.to).toBe(
`${preSubListText}\t1. a\n\t2. test`.length `${preSubListText}\t1. a\n\t2. test`.length
); );
});
it('should not preserve indentation when removing sublists', async () => {
const preSubListText = '# List test\n * This\n * is\n';
const initialDocText = `${preSubListText}\t1. a\n\t2. test\n * of list toggling`;
const editor = await createEditor(
initialDocText,
EditorSelection.range(preSubListText.length, `${preSubListText}\t1. a\n\t2. test`.length),
['ATXHeading1', 'BulletList', 'OrderedList']
);
// Indentation should not be preserved when removing lists // Indentation should not be preserved when removing lists
toggleList(ListType.OrderedList)(editor); toggleList(ListType.OrderedList)(editor);
@ -102,51 +118,47 @@ describe('markdownCommands.toggleList', () => {
'# List test\n * This\n * is\na\ntest\n * of list toggling' '# List test\n * This\n * is\na\ntest\n * of list toggling'
); );
// The below test: // Put the cursor in the middle of the list
// `expect(editor.state.doc.toString()).toBe(expectedChecklistPart)` editor.dispatch({ selection: EditorSelection.cursor(preSubListText.length) });
// randomly fails on CI, so disabling it for now.
// Sublists should be changed
toggleList(ListType.CheckList)(editor);
const expectedChecklistPart =
'# List test\n - [ ] This\n - [ ] is\n - [ ] a\n - [ ] test\n - [ ] of list toggling';
expect(editor.state.doc.toString()).toBe(
expectedChecklistPart
);
// // Put the cursor in the middle of the list editor.dispatch({ selection: EditorSelection.cursor(editor.state.doc.length) });
// editor.dispatch({ selection: EditorSelection.cursor(preSubListText.length) }); editor.dispatch(editor.state.replaceSelection('\n\n\n'));
// // Sublists should be changed // toggleList should also create a new list if the cursor is on an empty line.
// toggleList(ListType.CheckList)(editor); toggleList(ListType.OrderedList)(editor);
// const expectedChecklistPart = editor.dispatch(editor.state.replaceSelection('Test.\n2. Test2\n3. Test3'));
// '# List test\n - [ ] This\n - [ ] is\n - [ ] a\n - [ ] test\n - [ ] of list toggling';
// expect(editor.state.doc.toString()).toBe(
// expectedChecklistPart
// );
// editor.dispatch({ selection: EditorSelection.cursor(editor.state.doc.length) }); expect(editor.state.doc.toString()).toBe(
// editor.dispatch(editor.state.replaceSelection('\n\n\n')); `${expectedChecklistPart}\n\n\n1. Test.\n2. Test2\n3. Test3`
);
// // toggleList should also create a new list if the cursor is on an empty line. toggleList(ListType.CheckList)(editor);
// toggleList(ListType.OrderedList)(editor); expect(editor.state.doc.toString()).toBe(
// editor.dispatch(editor.state.replaceSelection('Test.\n2. Test2\n3. Test3')); `${expectedChecklistPart}\n\n\n- [ ] Test.\n- [ ] Test2\n- [ ] Test3`
);
// expect(editor.state.doc.toString()).toBe( // The entire checklist should have been selected (and thus will now be indented)
// `${expectedChecklistPart}\n\n\n1. Test.\n2. Test2\n3. Test3` increaseIndent(editor);
// ); expect(editor.state.doc.toString()).toBe(
`${expectedChecklistPart}\n\n\n\t- [ ] Test.\n\t- [ ] Test2\n\t- [ ] Test3`
// toggleList(ListType.CheckList)(editor); );
// expect(editor.state.doc.toString()).toBe(
// `${expectedChecklistPart}\n\n\n- [ ] Test.\n- [ ] Test2\n- [ ] Test3`
// );
// // The entire checklist should have been selected (and thus will now be indented)
// increaseIndent(editor);
// expect(editor.state.doc.toString()).toBe(
// `${expectedChecklistPart}\n\n\n\t- [ ] Test.\n\t- [ ] Test2\n\t- [ ] Test3`
// );
}); });
it('should toggle a numbered list without changing its sublists', () => { it('should toggle a numbered list without changing its sublists', async () => {
const initialDocText = '1. Foo\n2. Bar\n3. Baz\n\t- Test\n\t- of\n\t- sublists\n4. Foo'; const initialDocText = '1. Foo\n2. Bar\n3. Baz\n\t- Test\n\t- of\n\t- sublists\n4. Foo';
const editor = createEditor( const editor = await createEditor(
initialDocText, initialDocText,
EditorSelection.cursor(0) EditorSelection.cursor(0),
['OrderedList', 'BulletList']
); );
toggleList(ListType.CheckList)(editor); toggleList(ListType.CheckList)(editor);
@ -155,12 +167,13 @@ describe('markdownCommands.toggleList', () => {
); );
}); });
it('should toggle a sublist without changing the parent list', () => { it('should toggle a sublist without changing the parent list', async () => {
const initialDocText = '1. This\n2. is\n3. '; const initialDocText = '1. This\n2. is\n3. ';
const editor = createEditor( const editor = await createEditor(
initialDocText, initialDocText,
EditorSelection.cursor(initialDocText.length) EditorSelection.cursor(initialDocText.length),
['OrderedList']
); );
increaseIndent(editor); increaseIndent(editor);
@ -177,11 +190,12 @@ describe('markdownCommands.toggleList', () => {
); );
}); });
it('should toggle lists properly within block quotes', () => { it('should toggle lists properly within block quotes', async () => {
const preSubListText = '> # List test\n> * This\n> * is\n'; const preSubListText = '> # List test\n> * This\n> * is\n';
const initialDocText = `${preSubListText}> \t* a\n> \t* test\n> * of list toggling`; const initialDocText = `${preSubListText}> \t* a\n> \t* test\n> * of list toggling`;
const editor = createEditor( const editor = await createEditor(
initialDocText, EditorSelection.cursor(preSubListText.length + 3) initialDocText, EditorSelection.cursor(preSubListText.length + 3),
['BlockQuote', 'BulletList']
); );
toggleList(ListType.OrderedList)(editor); toggleList(ListType.OrderedList)(editor);

View File

@ -1,24 +1,17 @@
import { markdown } from '@codemirror/lang-markdown'; /**
* @jest-environment jsdom
*/
import { syntaxTree } from '@codemirror/language'; import { syntaxTree } from '@codemirror/language';
import { SyntaxNode } from '@lezer/common'; import { SyntaxNode } from '@lezer/common';
import { EditorState } from '@codemirror/state'; import { EditorSelection, EditorState } from '@codemirror/state';
import { blockMathTagName, inlineMathTagName, MarkdownMathExtension } from './markdownMathParser'; import { blockMathTagName, inlineMathContentTagName, inlineMathTagName } from './markdownMathParser';
import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown';
import forceFullParse from './testUtil/forceFullParse'; import createEditor from './testUtil/createEditor';
// Creates an EditorState with math and markdown extensions // Creates an EditorState with math and markdown extensions
const createEditorState = (initialText: string): EditorState => { const createEditorState = async (initialText: string, expectedTags: string[]): Promise<EditorState> => {
const editorState = EditorState.create({ return (await createEditor(initialText, EditorSelection.cursor(0), expectedTags)).state;
doc: initialText,
extensions: [
markdown({
extensions: [MarkdownMathExtension, GithubFlavoredMarkdownExt],
}),
],
});
forceFullParse(editorState);
return editorState;
}; };
// Returns a list of all nodes with the given name in the given editor's syntax tree. // Returns a list of all nodes with the given name in the given editor's syntax tree.
@ -38,31 +31,29 @@ const findNodesWithName = (editor: EditorState, nodeName: string) => {
describe('markdownMathParser', () => { describe('markdownMathParser', () => {
// Disable flaky test - randomly fails on line `expect(inlineMathContentNodes.length).toBe(0);` it('should parse inline math that contains space characters, numbers, and symbols', async () => {
const documentText = '$3 + 3$';
const editor = await createEditorState(documentText, [inlineMathTagName, 'number']);
const inlineMathNodes = findNodesWithName(editor, inlineMathTagName);
const inlineMathContentNodes = findNodesWithName(editor, inlineMathContentTagName);
// it('should parse inline math that contains space characters, numbers, and symbols', () => { // There should only be one inline node
// const documentText = '$3 + 3$'; expect(inlineMathNodes.length).toBe(1);
// const editor = createEditorState(documentText);
// const inlineMathNodes = findNodesWithName(editor, inlineMathTagName);
// const inlineMathContentNodes = findNodesWithName(editor, inlineMathContentTagName);
// // There should only be one inline node expect(inlineMathNodes[0].from).toBe(0);
// expect(inlineMathNodes.length).toBe(1); expect(inlineMathNodes[0].to).toBe(documentText.length);
// expect(inlineMathNodes[0].from).toBe(0); // The content tag should be replaced by the internal sTeX parser
// expect(inlineMathNodes[0].to).toBe(documentText.length); expect(inlineMathContentNodes.length).toBe(0);
});
// // The content tag should be replaced by the internal sTeX parser it('should parse comment within multi-word inline math', async () => {
// expect(inlineMathContentNodes.length).toBe(0);
// });
it('should parse comment within multi-word inline math', () => {
const beforeMath = '# Testing!\n\nThis is a test of '; const beforeMath = '# Testing!\n\nThis is a test of ';
const mathRegion = '$\\TeX % TeX Comment!$'; const mathRegion = '$\\TeX % TeX Comment!$';
const afterMath = ' formatting.'; const afterMath = ' formatting.';
const documentText = `${beforeMath}${mathRegion}${afterMath}`; const documentText = `${beforeMath}${mathRegion}${afterMath}`;
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, [inlineMathTagName, 'comment']);
const inlineMathNodes = findNodesWithName(editor, inlineMathTagName); const inlineMathNodes = findNodesWithName(editor, inlineMathTagName);
const blockMathNodes = findNodesWithName(editor, blockMathTagName); const blockMathNodes = findNodesWithName(editor, blockMathTagName);
const commentNodes = findNodesWithName(editor, 'comment'); const commentNodes = findNodesWithName(editor, 'comment');
@ -75,30 +66,30 @@ describe('markdownMathParser', () => {
expect(inlineMathNodes[0].to).toBe(beforeMath.length + mathRegion.length); expect(inlineMathNodes[0].to).toBe(beforeMath.length + mathRegion.length);
}); });
it('shouldn\'t start inline math if there is no ending $', () => { it('shouldn\'t start inline math if there is no ending $', async () => {
const documentText = 'This is a $test\n\nof inline math$...'; const documentText = '*This* is a $test\n\nof inline math$...';
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, ['Emphasis']);
const inlineMathNodes = findNodesWithName(editor, inlineMathTagName); const inlineMathNodes = findNodesWithName(editor, inlineMathTagName);
// Math should end if there is no matching '$'. // Math should end if there is no matching '$'.
expect(inlineMathNodes.length).toBe(0); expect(inlineMathNodes.length).toBe(0);
}); });
it('shouldn\'t start if math would have a space just after the $', () => { it('shouldn\'t start if math would have a space just after the $', async () => {
const documentText = 'This is a $ test of inline math$...\n\n$Testing... $...'; const documentText = 'This *is* a $ test of inline math$...\n\n$Testing... $...';
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, ['Emphasis']);
expect(findNodesWithName(editor, inlineMathTagName).length).toBe(0); expect(findNodesWithName(editor, inlineMathTagName).length).toBe(0);
}); });
it('shouldn\'t start inline math if $ is escaped', () => { it('shouldn\'t start inline math if $ is escaped', async () => {
const documentText = 'This is a \\$test of inline math$...'; const documentText = 'This is a \\$test of inline math$... **Testing...**';
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, ['StrongEmphasis']);
expect(findNodesWithName(editor, inlineMathTagName).length).toBe(0); expect(findNodesWithName(editor, inlineMathTagName).length).toBe(0);
}); });
it('should correctly parse document containing just block math', () => { it('should correctly parse document containing just block math', async () => {
const documentText = '$$\n\t\\{ 1, 1, 2, 3, 5, ... \\}\n$$'; const documentText = '$$\n\t\\{ 1, 1, 2, 3, 5, ... \\} % Comment\n$$';
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, [blockMathTagName, 'comment']);
const inlineMathNodes = findNodesWithName(editor, inlineMathTagName); const inlineMathNodes = findNodesWithName(editor, inlineMathTagName);
const blockMathNodes = findNodesWithName(editor, blockMathTagName); const blockMathNodes = findNodesWithName(editor, blockMathTagName);
@ -109,10 +100,10 @@ describe('markdownMathParser', () => {
expect(blockMathNodes[0].to).toBe(documentText.length); expect(blockMathNodes[0].to).toBe(documentText.length);
}); });
it('should correctly parse comment in block math', () => { it('should correctly parse comment in block math', async () => {
const startingText = '$$ % Testing...\n\t\\text{Test.}\n$$'; const startingText = '$$ % Testing...\n\t\\text{Test.}\n$$';
const afterMath = '\nTest.'; const afterMath = '\nTest.';
const editor = createEditorState(startingText + afterMath); const editor = await createEditorState(startingText + afterMath, ['comment', blockMathTagName]);
const inlineMathNodes = findNodesWithName(editor, inlineMathTagName); const inlineMathNodes = findNodesWithName(editor, inlineMathTagName);
const blockMathNodes = findNodesWithName(editor, blockMathTagName); const blockMathNodes = findNodesWithName(editor, blockMathTagName);
const texParserComments = findNodesWithName(editor, 'comment'); const texParserComments = findNodesWithName(editor, 'comment');
@ -130,10 +121,10 @@ describe('markdownMathParser', () => {
}); });
}); });
it('should extend block math without ending tag to end of document', () => { it('should extend block math without ending tag to end of document', async () => {
const beforeMath = '# Testing...\n\n'; const beforeMath = '# Testing...\n\n';
const documentText = `${beforeMath}$$\n\t\\text{Testing...}\n\n\t3 + 3 = 6`; const documentText = `${beforeMath}$$\n\t\\text{Testing...}\n\n\t3 + 3 = 6 % Comment`;
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, ['ATXHeading1', blockMathTagName, 'comment']);
const blockMathNodes = findNodesWithName(editor, blockMathTagName); const blockMathNodes = findNodesWithName(editor, blockMathTagName);
expect(blockMathNodes.length).toBe(1); expect(blockMathNodes.length).toBe(1);
@ -141,9 +132,9 @@ describe('markdownMathParser', () => {
expect(blockMathNodes[0].to).toBe(documentText.length); expect(blockMathNodes[0].to).toBe(documentText.length);
}); });
it('should parse block math declared on a single line', () => { it('should parse block math declared on a single line', async () => {
const documentText = '$$ Test. $$'; const documentText = '$$ Test. $$';
const editor = createEditorState(documentText); const editor = await createEditorState(documentText, [blockMathTagName]);
const blockMathNodes = findNodesWithName(editor, blockMathTagName); const blockMathNodes = findNodesWithName(editor, blockMathTagName);
expect(blockMathNodes.length).toBe(1); expect(blockMathNodes.length).toBe(1);

View File

@ -1,13 +1,19 @@
import { markdown } from '@codemirror/lang-markdown'; import { markdown } from '@codemirror/lang-markdown';
import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown'; import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown';
import { indentUnit } from '@codemirror/language'; import { indentUnit, syntaxTree } from '@codemirror/language';
import { SelectionRange, EditorSelection, EditorState } from '@codemirror/state'; import { SelectionRange, EditorSelection, EditorState } from '@codemirror/state';
import { EditorView } from '@codemirror/view'; import { EditorView } from '@codemirror/view';
import { MarkdownMathExtension } from '../markdownMathParser'; import { MarkdownMathExtension } from '../markdownMathParser';
import forceFullParse from './forceFullParse'; import forceFullParse from './forceFullParse';
import loadLangauges from './loadLanguages';
// Creates and returns a minimal editor with markdown extensions. Waits to return the editor
// until all syntax tree tags in `expectedSyntaxTreeTags` exist.
const createEditor = async (
initialText: string, initialSelection: SelectionRange, expectedSyntaxTreeTags: string[]
): Promise<EditorView> => {
await loadLangauges();
// Creates and returns a minimal editor with markdown extensions
const createEditor = (initialText: string, initialSelection: SelectionRange): EditorView => {
const editor = new EditorView({ const editor = new EditorView({
doc: initialText, doc: initialText,
selection: EditorSelection.create([initialSelection]), selection: EditorSelection.create([initialSelection]),
@ -20,7 +26,39 @@ const createEditor = (initialText: string, initialSelection: SelectionRange): Ed
], ],
}); });
forceFullParse(editor.state); let sawExpectedTagCount = 0;
while (sawExpectedTagCount < expectedSyntaxTreeTags.length) {
forceFullParse(editor.state);
sawExpectedTagCount = 0;
const seenTags = new Set<string>();
syntaxTree(editor.state).iterate({
from: 0,
to: editor.state.doc.length,
enter: (node) => {
for (const expectedTag of expectedSyntaxTreeTags) {
if (node.name === expectedTag) {
seenTags.add(node.name);
sawExpectedTagCount ++;
break;
}
}
},
});
if (sawExpectedTagCount < expectedSyntaxTreeTags.length) {
const missingTags = expectedSyntaxTreeTags.filter(tagName => {
return !seenTags.has(tagName);
});
console.warn(`Didn't find all expected tags. Missing ${missingTags}. Retrying...`);
await new Promise(resolve => {
setTimeout(resolve, 500);
});
}
}
return editor; return editor;
}; };

View File

@ -0,0 +1,12 @@
import syntaxHighlightingLanguages from '../syntaxHighlightingLanguages';
// Ensure languages we use are loaded. Without this, tests may randomly fail (LanguageDescriptions
// are loaded asyncronously, in the background).
const loadLangauges = async () => {
const allLanguages = syntaxHighlightingLanguages;
for (const lang of allLanguages) {
await lang.load();
}
};
export default loadLangauges;

View File

@ -81,11 +81,11 @@
"@codemirror/lang-markdown": "6.0.5", "@codemirror/lang-markdown": "6.0.5",
"@codemirror/lang-php": "6.0.1", "@codemirror/lang-php": "6.0.1",
"@codemirror/lang-rust": "6.0.1", "@codemirror/lang-rust": "6.0.1",
"@codemirror/language": "6.3.1", "@codemirror/language": "6.3.2",
"@codemirror/legacy-modes": "6.3.1", "@codemirror/legacy-modes": "6.3.1",
"@codemirror/search": "6.2.3", "@codemirror/search": "6.2.3",
"@codemirror/state": "6.1.4", "@codemirror/state": "6.1.4",
"@codemirror/view": "6.6.0", "@codemirror/view": "6.7.1",
"@joplin/tools": "~2.10", "@joplin/tools": "~2.10",
"@lezer/highlight": "1.1.3", "@lezer/highlight": "1.1.3",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",

View File

@ -3173,8 +3173,8 @@ __metadata:
linkType: hard linkType: hard
"@codemirror/autocomplete@npm:^6.0.0": "@codemirror/autocomplete@npm:^6.0.0":
version: 6.3.4 version: 6.4.0
resolution: "@codemirror/autocomplete@npm:6.3.4" resolution: "@codemirror/autocomplete@npm:6.4.0"
dependencies: dependencies:
"@codemirror/language": ^6.0.0 "@codemirror/language": ^6.0.0
"@codemirror/state": ^6.0.0 "@codemirror/state": ^6.0.0
@ -3185,7 +3185,7 @@ __metadata:
"@codemirror/state": ^6.0.0 "@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0 "@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0 "@lezer/common": ^1.0.0
checksum: dafb6b3dee11551ed7a2ec1d20fa05641abefe2e0b5da045d4a3383146bb04f0b9650448a378a5921cc183944d626482a608b71f3da5a036a881a873006b8dbf checksum: 3470fee01da60d3d71b8b4f8728629c0f0441e704b8b828592f98c000d75fdb2c9077727e82685626cf45b95cadbc0c1a03968261df2f0cfb4162418b5f4dd1f
languageName: node languageName: node
linkType: hard linkType: hard
@ -3250,7 +3250,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/lang-javascript@npm:6.1.1, @codemirror/lang-javascript@npm:^6.0.0": "@codemirror/lang-javascript@npm:6.1.1":
version: 6.1.1 version: 6.1.1
resolution: "@codemirror/lang-javascript@npm:6.1.1" resolution: "@codemirror/lang-javascript@npm:6.1.1"
dependencies: dependencies:
@ -3265,6 +3265,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/lang-javascript@npm:^6.0.0":
version: 6.1.2
resolution: "@codemirror/lang-javascript@npm:6.1.2"
dependencies:
"@codemirror/autocomplete": ^6.0.0
"@codemirror/language": ^6.0.0
"@codemirror/lint": ^6.0.0
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0
"@lezer/javascript": ^1.0.0
checksum: f4336b7efd44e4158b9979f0c23918184c897d0fe3e40b5414bd9243a9899ecdba4dfe13970fe5024a1894579af80cb4c5dd574c6c2b7bd7ff06d8c8cb88616b
languageName: node
linkType: hard
"@codemirror/lang-markdown@npm:6.0.5": "@codemirror/lang-markdown@npm:6.0.5":
version: 6.0.5 version: 6.0.5
resolution: "@codemirror/lang-markdown@npm:6.0.5" resolution: "@codemirror/lang-markdown@npm:6.0.5"
@ -3302,9 +3317,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/language@npm:6.3.1, @codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.3.0": "@codemirror/language@npm:6.3.2, @codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.3.0":
version: 6.3.1 version: 6.3.2
resolution: "@codemirror/language@npm:6.3.1" resolution: "@codemirror/language@npm:6.3.2"
dependencies: dependencies:
"@codemirror/state": ^6.0.0 "@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0 "@codemirror/view": ^6.0.0
@ -3312,7 +3327,7 @@ __metadata:
"@lezer/highlight": ^1.0.0 "@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0 "@lezer/lr": ^1.0.0
style-mod: ^4.0.0 style-mod: ^4.0.0
checksum: 349b9806e1e2ce5d99ba1f5815cc4772e6032f68c95718594e8335196ef0686bc6378db7cdd5f0fda57ba068eebf0ee413bb336e32cc1ff958a743190a0266da checksum: b70ed9b85d0bea79181c86e88a1f5c0bada30680ee1fe6a68efc01bc037c3d14f94a83602fc46cc4b4393589605ef7e986ed5174443502f3365dd61f883894fa
languageName: node languageName: node
linkType: hard linkType: hard
@ -3354,14 +3369,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/view@npm:6.6.0, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.2.2, @codemirror/view@npm:^6.6.0": "@codemirror/view@npm:6.7.1, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.2.2, @codemirror/view@npm:^6.6.0":
version: 6.6.0 version: 6.7.1
resolution: "@codemirror/view@npm:6.6.0" resolution: "@codemirror/view@npm:6.7.1"
dependencies: dependencies:
"@codemirror/state": ^6.1.4 "@codemirror/state": ^6.1.4
style-mod: ^4.0.0 style-mod: ^4.0.0
w3c-keyname: ^2.2.4 w3c-keyname: ^2.2.4
checksum: 9b007eedcf13e94ec7d9c30ee302e1a1fcd382bef2481bd9afa3a116458652983e745b40494eb29d80df1dca8f99e91dcb1e4eba37670c2553ffc90bef0933e7 checksum: 75a5846d61e63027e9bf1dfd0b507932934cb7650b7959c1191e68b161eb1756e9773f964c4331970b51864aef8f7954bc5cc8fdb51b0f6533de6c20568833ed
languageName: node languageName: node
linkType: hard linkType: hard
@ -4662,11 +4677,11 @@ __metadata:
"@codemirror/lang-markdown": 6.0.5 "@codemirror/lang-markdown": 6.0.5
"@codemirror/lang-php": 6.0.1 "@codemirror/lang-php": 6.0.1
"@codemirror/lang-rust": 6.0.1 "@codemirror/lang-rust": 6.0.1
"@codemirror/language": 6.3.1 "@codemirror/language": 6.3.2
"@codemirror/legacy-modes": 6.3.1 "@codemirror/legacy-modes": 6.3.1
"@codemirror/search": 6.2.3 "@codemirror/search": 6.2.3
"@codemirror/state": 6.1.4 "@codemirror/state": 6.1.4
"@codemirror/view": 6.6.0 "@codemirror/view": 6.7.1
"@joplin/lib": ~2.10 "@joplin/lib": ~2.10
"@joplin/react-native-saf-x": ~2.10 "@joplin/react-native-saf-x": ~2.10
"@joplin/renderer": ~2.10 "@joplin/renderer": ~2.10
@ -6023,12 +6038,12 @@ __metadata:
linkType: hard linkType: hard
"@lezer/css@npm:^1.0.0, @lezer/css@npm:^1.1.0": "@lezer/css@npm:^1.0.0, @lezer/css@npm:^1.1.0":
version: 1.1.0 version: 1.1.1
resolution: "@lezer/css@npm:1.1.0" resolution: "@lezer/css@npm:1.1.1"
dependencies: dependencies:
"@lezer/highlight": ^1.0.0 "@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0 "@lezer/lr": ^1.0.0
checksum: 5d2a176d7f4cf5076d8841af9b7bcafcbad5dd1b8f46fa1ad56c0fbf76f4bd4cd4ee0b1c4f1f1c9f8dba4fffb88908e64b5d7919c8706b35f575ddff8512ef31 checksum: a7e4893aacaa7f26d5679c77a640f401b37d14155cb54863aa91b59dfd220b280360a341c0fedafc65d31101de13a5ae33cf3876c352f2da528344dafdc9b3d7
languageName: node languageName: node
linkType: hard linkType: hard
@ -6042,13 +6057,13 @@ __metadata:
linkType: hard linkType: hard
"@lezer/html@npm:^1.1.0": "@lezer/html@npm:^1.1.0":
version: 1.2.0 version: 1.3.0
resolution: "@lezer/html@npm:1.2.0" resolution: "@lezer/html@npm:1.3.0"
dependencies: dependencies:
"@lezer/common": ^1.0.0 "@lezer/common": ^1.0.0
"@lezer/highlight": ^1.0.0 "@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0 "@lezer/lr": ^1.0.0
checksum: 737f6884328845100575c3bb9b0add622d00233d9d75f6bd201d37e31b990af371984b7ab91681bfe258234b77d486bc97f61a8ebdb4bd70a942f06a22b1aac1 checksum: e6efde94614a5b7ebf2713b244a110ef9025369561c7bf42fe6dd8f5877d2ee0c71f894b8b43d1284d23bf429fd3688ec3b6b0c2b8702df366c2b5e5cedc4c19
languageName: node languageName: node
linkType: hard linkType: hard
@ -6063,12 +6078,12 @@ __metadata:
linkType: hard linkType: hard
"@lezer/javascript@npm:^1.0.0": "@lezer/javascript@npm:^1.0.0":
version: 1.3.1 version: 1.4.0
resolution: "@lezer/javascript@npm:1.3.1" resolution: "@lezer/javascript@npm:1.4.0"
dependencies: dependencies:
"@lezer/highlight": ^1.0.0 "@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0 "@lezer/lr": ^1.0.0
checksum: bcf1a2ac84198f7caedf320d5222b6f4d39ece62d939ebe02b461bb175027c9b66d7be7feba51d3d8317bdd972c4fbdde3d9edbd60fa342b9e97db8ca1b63922 checksum: 36c64e8530feef9b937cf75f8833aa8c0f5c8c0812c55c53a133d1af5deb491dd80084397d5773e873db90ff717aede25b45fa827eead66400cb81b097567c42
languageName: node languageName: node
linkType: hard linkType: hard