From 9ef41a619c11b4f05b04ea6668e7923ca48d51ba Mon Sep 17 00:00:00 2001 From: metiftikci Date: Mon, 23 Dec 2024 22:45:58 +0000 Subject: [PATCH 1/4] fix textarea newline handle --- web_src/js/features/comp/EditorMarkdown.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts index 2af003ccb0a43..13af1c843a3b6 100644 --- a/web_src/js/features/comp/EditorMarkdown.ts +++ b/web_src/js/features/comp/EditorMarkdown.ts @@ -92,11 +92,12 @@ function handleNewline(textarea: HTMLTextAreaElement, e: Event) { if (!line) { // clear current line if we only have i.e. '1. ' and the user presses enter again to finish creating a list textarea.value = value.slice(0, lineStart) + value.slice(lineEnd); + textarea.setSelectionRange(selStart - prefix.length, selStart - prefix.length); } else { // start a new line with the same indention and prefix let newPrefix = prefix; // a simple approach, otherwise it needs to parse the lines after the current line - if (/^\d+\./.test(prefix)) newPrefix = `1. ${newPrefix.slice(newPrefix.indexOf('.') + 2)}`; + if (/^\d+\./.test(prefix)) newPrefix = `${Number(newPrefix.slice(0, newPrefix.indexOf('.'))) + 1}. `; newPrefix = newPrefix.replace('[x]', '[ ]'); const newLine = `\n${indention}${newPrefix}`; textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd); From a2203dd962b0906f724e63f7b4ba897ef5bd0f1b Mon Sep 17 00:00:00 2001 From: metiftikci Date: Mon, 23 Dec 2024 23:12:00 +0000 Subject: [PATCH 2/4] fix task list on numbered list --- web_src/js/features/comp/EditorMarkdown.test.ts | 3 ++- web_src/js/features/comp/EditorMarkdown.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/comp/EditorMarkdown.test.ts b/web_src/js/features/comp/EditorMarkdown.test.ts index acd496bed6d1b..913b39ec737d4 100644 --- a/web_src/js/features/comp/EditorMarkdown.test.ts +++ b/web_src/js/features/comp/EditorMarkdown.test.ts @@ -20,8 +20,9 @@ test('EditorMarkdown', () => { testInput('1. ', ''); testInput('- x', '- x\n- '); + testInput('1. foo', '1. foo\n2. '); testInput('- [ ]', '- [ ]\n- '); testInput('- [ ] foo', '- [ ] foo\n- [ ] '); testInput('* [x] foo', '* [x] foo\n* [ ] '); - testInput('1. [x] foo', '1. [x] foo\n1. [ ] '); + testInput('1. [x] foo', '1. [x] foo\n2. [ ] '); }); diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts index 13af1c843a3b6..41ccaf90343e5 100644 --- a/web_src/js/features/comp/EditorMarkdown.ts +++ b/web_src/js/features/comp/EditorMarkdown.ts @@ -97,7 +97,8 @@ function handleNewline(textarea: HTMLTextAreaElement, e: Event) { // start a new line with the same indention and prefix let newPrefix = prefix; // a simple approach, otherwise it needs to parse the lines after the current line - if (/^\d+\./.test(prefix)) newPrefix = `${Number(newPrefix.slice(0, newPrefix.indexOf('.'))) + 1}. `; + const numberedListMatch = /^(\d+)\.( \[[ x]\])? /.exec(prefix); + if (numberedListMatch) newPrefix = `${Number(numberedListMatch[1]) + 1}.${numberedListMatch[2] ?? ''} `; newPrefix = newPrefix.replace('[x]', '[ ]'); const newLine = `\n${indention}${newPrefix}`; textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd); From f5048c1005f4b50cdf46ac24f2e867b0bf6c27cc Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 24 Dec 2024 10:18:07 +0800 Subject: [PATCH 3/4] fine tune tests --- .../js/features/comp/EditorMarkdown.test.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/web_src/js/features/comp/EditorMarkdown.test.ts b/web_src/js/features/comp/EditorMarkdown.test.ts index 913b39ec737d4..af1fa48cd4709 100644 --- a/web_src/js/features/comp/EditorMarkdown.test.ts +++ b/web_src/js/features/comp/EditorMarkdown.test.ts @@ -4,13 +4,24 @@ test('EditorMarkdown', () => { const textarea = document.createElement('textarea'); initTextareaMarkdown(textarea); - const testInput = (value, expected) => { - textarea.value = value; - textarea.setSelectionRange(value.length, value.length); + type ValueWithCursor = string | { + value: string; + pos: number; + } + const testInput = (input: ValueWithCursor, result: ValueWithCursor) => { + const intputValue = typeof input === 'string' ? input : input.value; + const inputPos = typeof input === 'string' ? intputValue.length : input.pos; + textarea.value = intputValue; + textarea.setSelectionRange(inputPos, inputPos); + const e = new KeyboardEvent('keydown', {key: 'Enter', cancelable: true}); textarea.dispatchEvent(e); - if (!e.defaultPrevented) textarea.value += '\n'; - expect(textarea.value).toEqual(expected); + if (!e.defaultPrevented) textarea.value += '\n'; // simulate default behavior + + const expectedValue = typeof result === 'string' ? result : result.value; + const expectedPos = typeof result === 'string' ? expectedValue.length : result.pos; + expect(textarea.value).toEqual(expectedValue); + expect(textarea.selectionStart).toEqual(expectedPos); }; testInput('-', '-\n'); @@ -18,9 +29,11 @@ test('EditorMarkdown', () => { testInput('- ', ''); testInput('1. ', ''); + testInput({value: '1. \n2. ', pos: 3}, {value: '\n2. ', pos: 0}); testInput('- x', '- x\n- '); testInput('1. foo', '1. foo\n2. '); + testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n2. \n2. b\n3. c', pos: 8}); testInput('- [ ]', '- [ ]\n- '); testInput('- [ ] foo', '- [ ] foo\n- [ ] '); testInput('* [x] foo', '* [x] foo\n* [ ] '); From 8b16a5e22ee2e636db8c1f414d01aa1dbaa25501 Mon Sep 17 00:00:00 2001 From: metiftikci Date: Tue, 24 Dec 2024 20:26:01 +0000 Subject: [PATCH 4/4] revert increase list line number --- web_src/js/features/comp/EditorMarkdown.test.ts | 6 +++--- web_src/js/features/comp/EditorMarkdown.ts | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/web_src/js/features/comp/EditorMarkdown.test.ts b/web_src/js/features/comp/EditorMarkdown.test.ts index af1fa48cd4709..7b4b44e83cb56 100644 --- a/web_src/js/features/comp/EditorMarkdown.test.ts +++ b/web_src/js/features/comp/EditorMarkdown.test.ts @@ -32,10 +32,10 @@ test('EditorMarkdown', () => { testInput({value: '1. \n2. ', pos: 3}, {value: '\n2. ', pos: 0}); testInput('- x', '- x\n- '); - testInput('1. foo', '1. foo\n2. '); - testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n2. \n2. b\n3. c', pos: 8}); + testInput('1. foo', '1. foo\n1. '); + testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n1. \n2. b\n3. c', pos: 8}); testInput('- [ ]', '- [ ]\n- '); testInput('- [ ] foo', '- [ ] foo\n- [ ] '); testInput('* [x] foo', '* [x] foo\n* [ ] '); - testInput('1. [x] foo', '1. [x] foo\n2. [ ] '); + testInput('1. [x] foo', '1. [x] foo\n1. [ ] '); }); diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts index 41ccaf90343e5..5e2ef121f5297 100644 --- a/web_src/js/features/comp/EditorMarkdown.ts +++ b/web_src/js/features/comp/EditorMarkdown.ts @@ -97,8 +97,7 @@ function handleNewline(textarea: HTMLTextAreaElement, e: Event) { // start a new line with the same indention and prefix let newPrefix = prefix; // a simple approach, otherwise it needs to parse the lines after the current line - const numberedListMatch = /^(\d+)\.( \[[ x]\])? /.exec(prefix); - if (numberedListMatch) newPrefix = `${Number(numberedListMatch[1]) + 1}.${numberedListMatch[2] ?? ''} `; + if (/^\d+\./.test(prefix)) newPrefix = `1. ${newPrefix.slice(newPrefix.indexOf('.') + 2)}`; newPrefix = newPrefix.replace('[x]', '[ ]'); const newLine = `\n${indention}${newPrefix}`; textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd);