Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions packages/core/src/extensions/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,55 @@ describe('commands', () => {
fixture.editor.commands.cycleCheckableList()
expect(docToMarkdown(fixture.doc)).toBe('+ [ ] outer\n - [ ] inner\n')
})

it('cycleOrderedList cycles plain content through bullet, ordered, and text', () => {
using fixture = setupFixture()
const { n } = fixture
fixture.set(n.doc(n.paragraph('todo<a>')))

expect(docToMarkdown(fixture.doc).trim()).toMatchInlineSnapshot(`"todo"`)
fixture.editor.commands.cycleOrderedList()
expect(docToMarkdown(fixture.doc).trim()).toMatchInlineSnapshot(`"- todo"`)
fixture.editor.commands.cycleOrderedList()
expect(docToMarkdown(fixture.doc).trim()).toMatchInlineSnapshot(`"1. todo"`)
fixture.editor.commands.cycleOrderedList()
expect(docToMarkdown(fixture.doc).trim()).toMatchInlineSnapshot(`"todo"`)
fixture.editor.commands.cycleOrderedList()
expect(docToMarkdown(fixture.doc).trim()).toMatchInlineSnapshot(`"- todo"`)
})

it('cycleOrderedList converts a task to a plain bullet first', () => {
using fixture = setupFixture()
const { n } = fixture
fixture.set(n.doc(n.list({ kind: 'task', marker: '+', checked: true }, n.paragraph('done<a>'))))

fixture.editor.commands.cycleOrderedList()

expect(fixture.doc.child(0).attrs).toMatchObject({
kind: 'bullet',
checked: false,
})
expect(docToMarkdown(fixture.doc)).toBe('- done\n')
})

it('cycleOrderedList changes only the closest nested list', () => {
using fixture = setupFixture()
const { n } = fixture
fixture.set(
n.doc(
n.list(
{ kind: 'bullet' },
n.paragraph('outer'),
n.list({ kind: 'bullet' }, n.paragraph('inner<a>')),
),
),
)

fixture.editor.commands.cycleOrderedList()
expect(docToMarkdown(fixture.doc)).toBe('- outer\n 1. inner\n')
fixture.editor.commands.cycleOrderedList()
expect(docToMarkdown(fixture.doc)).toBe('- outer\n\n inner\n')
})
})

describe('keymap', () => {
Expand Down
44 changes: 32 additions & 12 deletions packages/core/src/extensions/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import {
type ListAttrs,
} from '@prosekit/extensions/list'
import type { ProseMirrorNode } from '@prosekit/pm/model'
import { Plugin } from '@prosekit/pm/state'
import type { Command, EditorState } from '@prosekit/pm/state'
import { Plugin } from '@prosekit/pm/state'
import {
createListRenderingPlugin,
createSafariInputMethodWorkaroundPlugin,
Expand Down Expand Up @@ -300,8 +300,11 @@ function getListAttrsAtSelection(state: EditorState): MeowdownListAttrs | null {
}

/**
* Cycle the selected block between square and circle checkbox tasks. Non-task
* content becomes an unchecked square task; shape changes preserve checked state.
* Cycle the selected block between square and circle checkbox tasks.
*
* - A square task becomes a circle task, keeping its checked state;
* - A circle task becomes a square task, keeping its checked state;
* - Other content becomes an unchecked square task.
*/
function cycleCheckableList(): Command {
return (state, dispatch, view) => {
Expand All @@ -316,11 +319,35 @@ function cycleCheckableList(): Command {
}
}

function defineTaskCommands() {
/**
* Cycle the selected block between a bullet list, an ordered list, and no list.
*
* - A bullet list becomes an ordered list;
* - An ordered list unwraps;
* - Other content, including a task, becomes a bullet list.
*/
function cycleOrderedList(): Command {
return (state, dispatch, view) => {
const attrs = getListAttrsAtSelection(state)
const next: MeowdownListAttrs =
attrs?.kind === 'bullet' || attrs?.kind === 'ordered'
? { kind: 'ordered', marker: null, checked: false, collapsed: false }
: { kind: 'bullet', marker: null, checked: false, collapsed: false }
return toggleList<MeowdownListAttrs>(next)(state, dispatch, view)
}
}

function toggleListCollapsed(): Command {
return createToggleCollapsedCommand({ isToggleable: isCollapsibleBullet })
}

function defineMeowdownListCommands() {
return defineCommands({
cycleCheckableList,
cycleOrderedList,
wrapInCircleTask,
wrapInSquareTask,
toggleListCollapsed,
})
}

Expand Down Expand Up @@ -413,12 +440,6 @@ function defineMeowdownListPlugins(): PlainExtension {
])
}

function defineCollapseCommands() {
return defineCommands({
toggleListCollapsed: () => createToggleCollapsedCommand({ isToggleable: isCollapsibleBullet }),
})
}

function defineMeowdownListKeymap(): PlainExtension {
return defineKeymap({
'Mod-Enter': rotateSquareTask(),
Expand Down Expand Up @@ -450,7 +471,6 @@ export function defineMeowdownList() {
defineListMarkerAttr(),
defineListTaskMarkerAttr(),
defineListMarkerGapAttr(),
defineTaskCommands(),
defineCollapseCommands(),
defineMeowdownListCommands(),
)
}
Loading