Skip to content

Commit

Permalink
Correctly delete immutable and segmented entity content when at the e…
Browse files Browse the repository at this point in the history
…dge of a selection. Fixes facebookarchive#1063.
  • Loading branch information
mmmoussa committed Mar 11, 2017
1 parent 82d396d commit 99e943b
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 21 deletions.
48 changes: 34 additions & 14 deletions src/model/modifier/DraftModifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,29 +138,49 @@ var DraftModifier = {
rangeToRemove: SelectionState,
removalDirection: DraftRemovalDirection
): ContentState {
let startKey, endKey, startBlock, endBlock;
startKey = removalDirection === 'forward'
? rangeToRemove.getAnchorKey()
: rangeToRemove.getFocusKey();
endKey = removalDirection === 'forward'
? rangeToRemove.getFocusKey()
: rangeToRemove.getAnchorKey();
startBlock = contentState.getBlockForKey(startKey);
endBlock = contentState.getBlockForKey(endKey);
const startOffset = rangeToRemove.getStartOffset();
const endOffset = rangeToRemove.getEndOffset();

const startEntityKey = startBlock.getEntityAt(startOffset);
const endEntityKey = endBlock.getEntityAt(endOffset - 1);

// Check whether the selection state overlaps with a single entity.
// If so, try to remove the appropriate substring of the entity text.
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) {
var key = rangeToRemove.getAnchorKey();
var startOffset = rangeToRemove.getStartOffset();
var endOffset = rangeToRemove.getEndOffset();
var block = contentState.getBlockForKey(key);

var startEntity = block.getEntityAt(startOffset);
var endEntity = block.getEntityAt(endOffset - 1);
if (startEntity && startEntity === endEntity) {
var adjustedRemovalRange = getCharacterRemovalRange(
if (startKey === endKey) {
if (startEntityKey && startEntityKey === endEntityKey) {
const adjustedRemovalRange = getCharacterRemovalRange(
contentState.getEntityMap(),
block,
startBlock,
endBlock,
rangeToRemove,
removalDirection
removalDirection,
);
return removeRangeFromContentState(contentState, adjustedRemovalRange);
}
}
// Adjust the selection to properly delete segemented and immutable entities
const adjustedRemovalRange = getCharacterRemovalRange(
contentState.getEntityMap(),
startBlock,
endBlock,
rangeToRemove,
removalDirection,
);

var withoutEntities = removeEntitiesAtEdges(contentState, rangeToRemove);
return removeRangeFromContentState(withoutEntities, rangeToRemove);
var withoutEntities = removeEntitiesAtEdges(
contentState,
adjustedRemovalRange,
);
return removeRangeFromContentState(withoutEntities, adjustedRemovalRange);
},

splitBlock: function(
Expand Down
90 changes: 83 additions & 7 deletions src/model/modifier/getCharacterRemovalRange.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,93 @@ import type {EntityMap} from 'EntityMap';
*/
function getCharacterRemovalRange(
entityMap: EntityMap,
block: ContentBlock,
startBlock: ContentBlock,
endBlock: ContentBlock,
selectionState: SelectionState,
direction: DraftRemovalDirection
direction: DraftRemovalDirection,
): SelectionState {
var start = selectionState.getStartOffset();
var end = selectionState.getEndOffset();
var entityKey = block.getEntityAt(start);
if (!entityKey) {
var startEntityKey = startBlock.getEntityAt(start);
var endEntityKey = endBlock.getEntityAt(end);
if (!startEntityKey && !endEntityKey) {
return selectionState;
}
var newSelectionState = selectionState;
if (selectionState.getIsBackward()) {
newSelectionState = selectionState.merge({
anchorKey: selectionState.getFocusKey(),
anchorOffset: selectionState.getFocusOffset(),
focusKey: selectionState.getAnchorKey(),
focusOffset: selectionState.getAnchorOffset(),
isBackward: false,
});
}
if (startEntityKey && (startEntityKey === endEntityKey)) {
newSelectionState = getEntityRemovalRange(
entityMap,
startBlock,
newSelectionState,
direction,
startEntityKey,
);
} else if (startEntityKey && endEntityKey) {
const startSelectionState = getEntityRemovalRange(
entityMap,
startBlock,
newSelectionState,
direction,
startEntityKey,
);
const endSelectionState = getEntityRemovalRange(
entityMap,
endBlock,
newSelectionState,
direction,
endEntityKey,
);
newSelectionState = newSelectionState.merge({
anchorOffset: startSelectionState.getAnchorOffset(),
focusOffset: endSelectionState.getFocusOffset(),
isBackward: false,
});
} else if (startEntityKey) {
const startSelectionState = getEntityRemovalRange(
entityMap,
startBlock,
newSelectionState,
direction,
startEntityKey,
);
newSelectionState = newSelectionState.merge({
anchorOffset: startSelectionState.getStartOffset(),
isBackward: false,
});
} else if (endEntityKey) {
const endSelectionState = getEntityRemovalRange(
entityMap,
endBlock,
newSelectionState,
direction,
endEntityKey,
);
newSelectionState = newSelectionState.merge({
focusOffset: endSelectionState.getEndOffset(),
isBackward: false,
});
}
return newSelectionState;
}

function getEntityRemovalRange(
entityMap: EntityMap,
block: ContentBlock,
selectionState: SelectionState,
direction: DraftRemovalDirection,
entityKey: string,
): SelectionState {
var start = selectionState.getStartOffset();
var end = selectionState.getEndOffset();
var entity = entityMap.__get(entityKey);
var mutability = entity.getMutability();

Expand All @@ -56,12 +132,12 @@ function getCharacterRemovalRange(

// Find the entity range that overlaps with our removal range.
var entityRanges = getRangesForDraftEntity(block, entityKey).filter(
(range) => start < range.end && end > range.start
(range) => start < range.end && end > range.start,
);

invariant(
entityRanges.length == 1,
'There should only be one entity range within this removal range.'
'There should only be one entity range within this removal range.',
);

var entityRange = entityRanges[0];
Expand All @@ -82,7 +158,7 @@ function getCharacterRemovalRange(
end,
block.getText().slice(entityRange.start, entityRange.end),
entityRange.start,
direction
direction,
);

return selectionState.merge({
Expand Down

0 comments on commit 99e943b

Please sign in to comment.