Skip to content

Commit

Permalink
fix test and cursor.restore from clicking
Browse files Browse the repository at this point in the history
  • Loading branch information
dgreensp committed Oct 12, 2018
1 parent f652d92 commit d9591ce
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 53 deletions.
105 changes: 53 additions & 52 deletions blots/cursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,6 @@ class Cursor extends EmbedBlot {
restore() {
if (this.selection.composing || this.parent == null) return null;
const range = this.selection.getNativeRange();
let restoreText;
let start;
let end;
if (
range != null &&
range.start.node === this.textNode &&
range.end.node === this.textNode
) {
[restoreText, start, end] = [
this.textNode,
range.start.offset,
range.end.offset,
];
}
// Link format will insert text outside of anchor tag
while (
this.domNode.lastChild != null &&
Expand All @@ -83,49 +69,64 @@ class Cursor extends EmbedBlot {
this.domNode,
);
}
if (this.textNode.data !== Cursor.CONTENTS) {
const text = this.textNode.data.split(Cursor.CONTENTS).join('');
// Take text out of the cursor blot and add it to parchment and the DOM
// before removing the cursor blot (which will be reused in the future).
// Selection preservation note: After we return the selection range we
// want from this function, and before it is applied, TextBlots are
// optimized, with any TextBlot that follows another TextBlot being
// removed and its contents inserted into the previous one, and same with
// the underlying text nodes. Therefore, restoreText should not be a text
// node that will be optimized away. Another consideration here is that
// we don't want the optimization in editor.update to run, because it
// won't be able to figure out this change based on looking at the value
// of the mutated text node. Therefore, we don't insert this.textNode into
// the document, we keep it in the blot which is removed.
if (this.prev instanceof TextBlot) {
this.prev.insertAt(this.prev.length(), text);
if (restoreText) {
restoreText = this.prev.domNode;
start += this.prev.length();
end += this.prev.length();
}
} else {
const newTextNode = document.createTextNode(text);
this.parent.insertBefore(this.scroll.create(newTextNode), this);
if (restoreText) {
restoreText = newTextNode;
start -= 1;
end -= 1;

const prevTextBlot = this.prev instanceof TextBlot ? this.prev : null;
const prevTextLength = prevTextBlot ? prevTextBlot.length() : 0;
const nextTextBlot = this.next instanceof TextBlot ? this.next : null;
const nextText = nextTextBlot ? nextTextBlot.text : '';
const { textNode } = this;
// take text from inside this blot and reset it
const newText = textNode.data.split(Cursor.CONTENTS).join('');
textNode.data = Cursor.CONTENTS;

// proactively merge TextBlots around cursor so that optimization
// doesn't lose the cursor. the reason we are here in cursor.restore
// could be that the user clicked in prevTextBlot or nextTextBlot, or
// the user typed something.
let mergedTextBlot;
if (prevTextBlot) {
mergedTextBlot = prevTextBlot;
if (newText || nextTextBlot) {
prevTextBlot.insertAt(prevTextBlot.length(), newText + nextText);
if (nextTextBlot) {
nextTextBlot.remove();
}
}
this.textNode.data = Cursor.CONTENTS;
} else if (nextTextBlot) {
mergedTextBlot = nextTextBlot;
nextTextBlot.insertAt(0, newText);
} else {
const newTextNode = document.createTextNode(newText);
mergedTextBlot = this.scroll.create(newTextNode);
this.parent.insertBefore(mergedTextBlot, this);
}

this.remove();
if (restoreText) {
[start, end] = [start, end].map(offset => {
return Math.max(0, Math.min(restoreText.data.length, offset));
});
return {
startNode: restoreText,
startOffset: start,
endNode: restoreText,
endOffset: end,
if (range) {
// calculate selection to restore
const remapOffset = (node, offset) => {
if (prevTextBlot && node === prevTextBlot.domNode) {
return offset;
}
if (node === textNode) {
return prevTextLength + offset - 1;
}
if (nextTextBlot && node === nextTextBlot.domNode) {
return prevTextLength + newText.length + offset;
}
return null;
};

const start = remapOffset(range.start.node, range.start.offset);
const end = remapOffset(range.end.node, range.end.offset);
if (start !== null && end !== null) {
return {
startNode: mergedTextBlot.domNode,
startOffset: start,
endNode: mergedTextBlot.domNode,
endOffset: end,
};
}
}
return null;
}
Expand Down
10 changes: 9 additions & 1 deletion core/selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,15 @@ class Selection {
nativeRange.native.collapsed &&
nativeRange.start.node !== this.cursor.textNode
) {
this.cursor.restore();
const range = this.cursor.restore();
if (range) {
this.setNativeRange(
range.startNode,
range.startOffset,
range.endNode,
range.endOffset,
);
}
}
const args = [
Emitter.events.SELECTION_CHANGE,
Expand Down

0 comments on commit d9591ce

Please sign in to comment.