Fix unindent action when acting on multiple list items

This is caused by a limitation of surface fragments in that they
always expand to include insertions touching them. In this case
we build surface fragments contain adjacent listItems, so when
the first listItem is de-listed a transaction is first created to
split the list. This insertion of ('/list', 'list') is adjacent
to the second surface fragment and so becomes part of that fragment.

This then causes the wrong node to be passed to unindentListItem.

The workaround is to use the inner range of the listItem, not
the outer range.

Bug: 48390

Change-Id: I7418910412d292ef4953e294a97f66e48d6f776f
This commit is contained in:
Ed Sanders 2013-05-12 21:58:16 +01:00
parent da4ddbc4d9
commit 1261a5f471
2 changed files with 28 additions and 2 deletions

View file

@ -93,7 +93,7 @@ ve.IndentationAction.prototype.decrease = function () {
for ( i = 0; i < groups.length; i++ ) { for ( i = 0; i < groups.length; i++ ) {
group = groups[i]; group = groups[i];
if ( group.grandparent && group.grandparent.getType() === 'list' ) { if ( group.grandparent && group.grandparent.getType() === 'list' ) {
fragments.push( surfaceModel.getFragment( group.parent.getOuterRange(), true ) ); fragments.push( surfaceModel.getFragment( group.parent.getRange(), true ) );
decreased = true; decreased = true;
} }
} }
@ -101,7 +101,7 @@ ve.IndentationAction.prototype.decrease = function () {
// Process each fragment (their ranges are automatically adjusted on change) // Process each fragment (their ranges are automatically adjusted on change)
for ( i = 0; i < fragments.length; i++ ) { for ( i = 0; i < fragments.length; i++ ) {
this.unindentListItem( this.unindentListItem(
documentModel.getNodeFromOffset( fragments[i].getRange().start + 1 ) documentModel.getNodeFromOffset( fragments[i].getRange().start )
); );
} }
@ -117,8 +117,12 @@ ve.IndentationAction.prototype.decrease = function () {
* *
* @method * @method
* @param {ve.dm.ListItemNode} listItem List item to indent * @param {ve.dm.ListItemNode} listItem List item to indent
* @throws {Error} listItem must be a ve.dm.ListItemNode
*/ */
ve.IndentationAction.prototype.indentListItem = function ( listItem ) { ve.IndentationAction.prototype.indentListItem = function ( listItem ) {
if ( !( listItem instanceof ve.dm.ListItemNode ) ) {
throw new Error( 'listItem must be a ve.dm.ListItemNode' );
}
/* /*
* Indenting a list item is done as follows: * Indenting a list item is done as follows:
* 1. Wrap the listItem in a list and a listItem (<li> --> <li><ul><li>) * 1. Wrap the listItem in a list and a listItem (<li> --> <li><ul><li>)
@ -191,8 +195,12 @@ ve.IndentationAction.prototype.indentListItem = function ( listItem ) {
* *
* @method * @method
* @param {ve.dm.ListItemNode} listItem List item to unindent * @param {ve.dm.ListItemNode} listItem List item to unindent
* @throws {Error} listItem must be a ve.dm.ListItemNode
*/ */
ve.IndentationAction.prototype.unindentListItem = function ( listItem ) { ve.IndentationAction.prototype.unindentListItem = function ( listItem ) {
if ( !( listItem instanceof ve.dm.ListItemNode ) ) {
throw new Error( 'listItem must be a ve.dm.ListItemNode' );
}
/* /*
* Outdenting a list item is done as follows: * Outdenting a list item is done as follows:
* 1. Split the parent list to isolate the listItem in its own list * 1. Split the parent list to isolate the listItem in its own list

View file

@ -52,6 +52,24 @@ QUnit.test( 'decrease', 2, function ( assert ) {
delete data[12].internal; delete data[12].internal;
}, },
'msg': 'decrease indentation on partial selection of list item "Item 2"' 'msg': 'decrease indentation on partial selection of list item "Item 2"'
},
{
'range': new ve.Range( 3, 19 ),
'method': 'decrease',
'expectedSelection': new ve.Range( 1, 15 ),
'expectedData': function ( data ) {
data.splice( 0, 2 );
data.splice( 8, 2 );
data.splice( 16, 1, { 'type': 'list', 'attributes': { 'style': 'bullet' } } );
delete data[0].internal;
delete data[8].internal;
},
'expectedOriginalData': function ( data ) {
// generated: 'wrapper' is removed by the action and not restored by undo
delete data[2].internal;
delete data[12].internal;
},
'msg': 'decrease indentation on Items 1 & 2'
} }
]; ];