diff --git a/lib/models/state.js b/lib/models/state.js index 70ff2063c..f2ae395ab 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -21,7 +21,7 @@ const History = new Record({ */ const DEFAULTS = { - cursorMarks: new Set(), + cursorMarks: null, document: new Document(), selection: new Selection(), history: new History(), @@ -285,10 +285,7 @@ class State extends new Record(DEFAULTS) { */ get marks() { - const set = this.document.getMarksAtRange(this.selection) - return this.selection.isExpanded - ? set - : set.union(this.cursorMarks) + return this.cursorMarks || this.document.getMarksAtRange(this.selection) } /** @@ -674,27 +671,15 @@ class State extends new Record(DEFAULTS) { // Determine what the selection should be after inserting. if (selection.isExpanded) { - after = selection - .collapseToStart() - .moveForward(text.length) + after = selection.collapseToStart().moveForward(text.length) } else { after = selection.moveForward(text.length) } - // Insert the text. - document = document.insertTextAtRange(selection, text) - - // If there are any marks on the cursor, apply them. - if (cursorMarks.size) { - const range = after.extendBackward(text.length).normalize(document) - cursorMarks.forEach((mark) => { - document = document.markAtRange(range, mark) - }) - } - - // Update the selection and the state. + // Insert the text and update the selection. + document = document.insertTextAtRange(selection, text, cursorMarks) selection = after state = state.merge({ document, selection }) return state @@ -714,8 +699,9 @@ class State extends new Record(DEFAULTS) { // If the selection is collapsed, add the mark to the cursor instead. if (selection.isCollapsed) { if (typeof mark == 'string') mark = new Mark({ type: mark }) - cursorMarks = cursorMarks.add(mark) - return state.merge({ cursorMarks }) + const marks = document.getMarksAtRange(selection) + state = state.merge({ cursorMarks: marks.add(mark) }) + return state } document = document.markAtRange(selection, mark) @@ -839,8 +825,9 @@ class State extends new Record(DEFAULTS) { // If the selection is collapsed, remove the mark from the cursor instead. if (selection.isCollapsed) { if (typeof mark == 'string') mark = new Mark({ type: mark }) - cursorMarks = cursorMarks.remove(mark) - return state.merge({ cursorMarks }) + const marks = document.getMarksAtRange(selection) + state = state.merge({ cursorMarks: marks.remove(mark) }) + return state } document = document.unmarkAtRange(selection, mark) diff --git a/lib/models/text.js b/lib/models/text.js index 82e03405b..5895ad23c 100644 --- a/lib/models/text.js +++ b/lib/models/text.js @@ -110,15 +110,20 @@ class Text extends new Record(DEFAULTS) { /** * Insert text `string` at `index`. * - * @param {String} string * @param {Numbder} index + * @param {String} string + * @param {String} marks (optional) * @return {Text} text */ - insertText(string, index) { + insertText(index, string, marks) { let { characters } = this - const prev = index ? characters.get(index - 1) : null - const marks = prev ? prev.marks : Mark.createSet() + + if (!marks) { + const prev = index ? characters.get(index - 1) : null + marks = prev ? prev.marks : Mark.createSet() + } + const chars = Character.createList(string.split('').map((char) => { return { text: char, diff --git a/lib/models/transform.js b/lib/models/transform.js index 2b49d61bb..260c813a7 100644 --- a/lib/models/transform.js +++ b/lib/models/transform.js @@ -188,8 +188,9 @@ class Transform extends new Record(DEFAULT_PROPERTIES) { // If the selection has changed, clear any existing cursor marks. if (state.selection != selection) { - cursorMarks = cursorMarks.clear() - state = state.merge({ cursorMarks }) + state = state.merge({ + cursorMarks: null + }) } // Apply the "isNative" flag, which is used to allow for natively-handled diff --git a/lib/models/transforms.js b/lib/models/transforms.js index 3982a0b88..cfe9ad4d3 100644 --- a/lib/models/transforms.js +++ b/lib/models/transforms.js @@ -268,14 +268,15 @@ const Transforms = { }, /** - * Insert text `string` at a `range`. + * Insert text `string` at a `range`, with optional `marks`. * * @param {Selection} range * @param {String} string + * @param {Set} marks (optional) * @return {Node} node */ - insertTextAtRange(range, string) { + insertTextAtRange(range, string, marks) { let node = this // When still expanded, remove the current range first. @@ -287,7 +288,7 @@ const Transforms = { // Insert text at the range's offset. const { startKey, startOffset } = range let text = node.getDescendant(startKey) - text = text.insertText(string, startOffset) + text = text.insertText(startOffset, string, marks) node = node.updateDescendant(text) return node diff --git a/test/transforms/fixtures/mark/collapsed-selection-off-again/index.js b/test/transforms/fixtures/mark/collapsed-selection-off-again/index.js new file mode 100644 index 000000000..962dc40e3 --- /dev/null +++ b/test/transforms/fixtures/mark/collapsed-selection-off-again/index.js @@ -0,0 +1,20 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + .mark('bold') + .insertText('a') + .unmark('bold') + .insertText('b') + .apply() +} diff --git a/test/transforms/fixtures/mark/collapsed-selection-off-again/input.yaml b/test/transforms/fixtures/mark/collapsed-selection-off-again/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/collapsed-selection-off-again/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/collapsed-selection-off-again/output.yaml b/test/transforms/fixtures/mark/collapsed-selection-off-again/output.yaml new file mode 100644 index 000000000..94704aa20 --- /dev/null +++ b/test/transforms/fixtures/mark/collapsed-selection-off-again/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: a + marks: + - type: bold + - text: bword