1- import { Fragment , type Node , Slice } from 'prosemirror-model' ;
1+ import { Fragment , type Node } from 'prosemirror-model' ;
22import { Plugin , TextSelection , type Transaction } from 'prosemirror-state' ;
33import { findChildren , hasParentNode } from 'prosemirror-utils' ;
44
55import { getChildrenOfNode } from '../../../../utils' ;
6- import { isListItemNode , isListNode } from '../utils' ;
6+ import { isListItemNode , isListNode , liType } from '../utils' ;
77
88export const collapseListsPlugin = ( ) =>
99 new Plugin ( {
@@ -17,9 +17,15 @@ export const collapseListsPlugin = () =>
1717 if ( ! hasParentList ) return null ;
1818
1919 const { tr} = newState ;
20- const listNodes = findChildren ( tr . doc , isListNode , true ) ;
20+ let prevStepsCount = - 1 ;
21+ let currentStepsCount = 0 ;
2122
22- collapseEmptyListItems ( tr , listNodes ) ;
23+ // execute until there are no nested lists.
24+ while ( prevStepsCount !== currentStepsCount ) {
25+ const listNodes = findChildren ( tr . doc , isListNode , true ) ;
26+ prevStepsCount = currentStepsCount ;
27+ currentStepsCount = collapseEmptyListItems ( tr , listNodes ) ;
28+ }
2329
2430 return tr . docChanged ? tr : null ;
2531 } ,
@@ -28,13 +34,14 @@ export const collapseListsPlugin = () =>
2834export function collapseEmptyListItems (
2935 tr : Transaction ,
3036 nodes : ReturnType < typeof findChildren > ,
31- ) : void {
37+ ) : number {
38+ const stepsCountBefore = tr . steps . length ;
3239 nodes . reverse ( ) . forEach ( ( list ) => {
3340 const listNode = list . node ;
3441 const listPos = list . pos ;
35- const childrenOfNodes = getChildrenOfNode ( listNode ) . reverse ( ) ;
42+ const childrenOfList = getChildrenOfNode ( listNode ) . reverse ( ) ;
3643
37- childrenOfNodes . forEach ( ( { node : itemNode , offset} ) => {
44+ childrenOfList . forEach ( ( { node : itemNode , offset} ) => {
3845 if ( isListItemNode ( itemNode ) ) {
3946 const { firstChild} = itemNode ;
4047 const listItemNodePos = listPos + 1 + offset ;
@@ -45,25 +52,20 @@ export function collapseEmptyListItems(
4552 const nestedList = firstChild . content ;
4653
4754 // nodes at the same level as the list
48- const remainingNodes : Node [ ] = [ ] ;
49- itemNode . forEach ( ( child , _pos , index ) => {
50- if ( index > 0 ) {
51- remainingNodes . push ( child ) ;
52- }
53- } ) ;
55+ const remainingNodes = itemNode . content . content . slice ( 1 ) ;
5456
5557 const listItems = remainingNodes . length
5658 ? nestedList . append (
5759 Fragment . from (
58- tr . doc . type . schema . nodes . list_item . create ( null , remainingNodes ) ,
60+ liType ( tr . doc . type . schema ) . create ( null , remainingNodes ) ,
5961 ) ,
6062 )
6163 : nestedList ;
6264
6365 const mappedStart = tr . mapping . map ( listItemNodePos ) ;
6466 const mappedEnd = tr . mapping . map ( listItemNodePos + itemNode . nodeSize ) ;
6567
66- tr . replace ( mappedStart , mappedEnd , new Slice ( listItems , 0 , 0 ) ) ;
68+ tr . replaceWith ( mappedStart , mappedEnd , listItems ) ;
6769
6870 const closestTextNodePos = findClosestTextNodePos (
6971 tr . doc ,
@@ -76,6 +78,8 @@ export function collapseEmptyListItems(
7678 }
7779 } ) ;
7880 } ) ;
81+
82+ return tr . steps . length - stepsCountBefore ;
7983}
8084
8185function findClosestTextNodePos ( doc : Node , pos : number ) : number | null {
0 commit comments