diff --git a/src/nodes/OrderedList.ts b/src/nodes/OrderedList.ts index 0e5be87937e..7bab14d1830 100644 --- a/src/nodes/OrderedList.ts +++ b/src/nodes/OrderedList.ts @@ -3,6 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { Node } from '@tiptap/pm/model' +import type { MarkdownSerializerState } from 'prosemirror-markdown' + import { OrderedList as TiptapOrderedList } from '@tiptap/extension-list' import { toggleListCommand } from '../commands' @@ -23,6 +26,18 @@ const OrderedList = TiptapOrderedList.extend({ toggleOrderedList: toggleListCommand('orderedList', 'listItem'), } }, + + // Overwrite toMarkdown from prosemirror-markdown to preserve non-1 start numbers in lists + // @ts-expect-error - toMarkdown is a custom field not part of the official Tiptap API + toMarkdown(state: MarkdownSerializerState, node: Node) { + const start = node.attrs.start ?? 1 + const maxW = String(start + node.childCount - 1).length + const space = state.repeat(' ', maxW + 2) + state.renderList(node, space, (i) => { + const nStr = String(start + i) + return state.repeat(' ', maxW - nStr.length) + nStr + '. ' + }) + }, }) export default OrderedList diff --git a/src/tests/markdown.spec.js b/src/tests/markdown.spec.js index 1c85edef595..bee0ca217cf 100644 --- a/src/tests/markdown.spec.js +++ b/src/tests/markdown.spec.js @@ -55,6 +55,13 @@ describe('Markdown though editor', () => { }) test('ol', () => { expect(markdownThroughEditor('1. foo\n2. bar')).toBe('1. foo\n2. bar') + expect(markdownThroughEditor('0. foo\n1. bar')).toBe('0. foo\n1. bar') + // regression test for #4828 + expect(markdownThroughEditor('42. foo\n43. bar')).toBe('42. foo\n43. bar') + // for now we enforce sequential order in ordered lists + expect(markdownThroughEditor('2. foo\n19. bar\n2. baz')).toBe( + '2. foo\n3. bar\n4. baz', + ) }) test('paragraph', () => { // Test whitespace characters are untouched