From 2ea556e0b40897fb92785a956f1b97baa204d41e Mon Sep 17 00:00:00 2001
From: Bruno Windels <brunow@matrix.org>
Date: Wed, 4 Sep 2019 16:04:06 +0200
Subject: [PATCH] support update callback setting selection instead of caret

---
 .../views/rooms/BasicMessageComposer.js       | 10 +++---
 src/editor/caret.js                           | 33 ++++++++++++++++---
 src/editor/model.js                           |  7 +++-
 3 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js
index b1eb4f0746..3f07cf567e 100644
--- a/src/components/views/rooms/BasicMessageComposer.js
+++ b/src/components/views/rooms/BasicMessageComposer.js
@@ -20,7 +20,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import EditorModel from '../../../editor/model';
 import HistoryManager from '../../../editor/history';
-import {setCaretPosition} from '../../../editor/caret';
+import {setSelection} from '../../../editor/caret';
 import {
     formatRangeAsQuote,
     formatRangeAsCode,
@@ -115,11 +115,11 @@ export default class BasicMessageEditor extends React.Component {
         }
     }
 
-    _updateEditorState = (caret, inputType, diff) => {
+    _updateEditorState = (selection, inputType, diff) => {
         renderModel(this._editorRef, this.props.model);
-        if (caret) {
+        if (selection) { // set the caret/selection
             try {
-                setCaretPosition(this._editorRef, this.props.model, caret);
+                setSelection(this._editorRef, this.props.model, selection);
             } catch (err) {
                 console.error(err);
             }
@@ -133,7 +133,7 @@ export default class BasicMessageEditor extends React.Component {
             }
         }
         this.setState({autoComplete: this.props.model.autoComplete});
-        this.historyManager.tryPush(this.props.model, caret, inputType, diff);
+        this.historyManager.tryPush(this.props.model, selection, inputType, diff);
         TypingStore.sharedInstance().setSelfTyping(this.props.room.roomId, !this.props.model.isEmpty);
 
         if (this.props.onChange) {
diff --git a/src/editor/caret.js b/src/editor/caret.js
index 9b0fa14cfc..ed4f1b2a2e 100644
--- a/src/editor/caret.js
+++ b/src/editor/caret.js
@@ -16,12 +16,39 @@ limitations under the License.
 */
 
 import {needsCaretNodeBefore, needsCaretNodeAfter} from "./render";
+import Range from "./range";
+
+export function setSelection(editor, model, selection) {
+    if (selection instanceof Range) {
+        setDocumentRangeSelection(editor, model, selection);
+    } else {
+        setCaretPosition(editor, model, selection);
+    }
+}
+
+function setDocumentRangeSelection(editor, model, range) {
+    const sel = document.getSelection();
+    sel.removeAllRanges();
+    const selectionRange = document.createRange();
+    const start = getNodeAndOffsetForPosition(editor, model, range.start);
+    selectionRange.setStart(start.node, start.offset);
+    const end = getNodeAndOffsetForPosition(editor, model, range.end);
+    selectionRange.setEnd(end.node, end.offset);
+    sel.addRange(selectionRange);
+}
 
 export function setCaretPosition(editor, model, caretPosition) {
     const sel = document.getSelection();
     sel.removeAllRanges();
     const range = document.createRange();
-    const {offset, lineIndex, nodeIndex} = getLineAndNodePosition(model, caretPosition);
+    const {node, offset} = getNodeAndOffsetForPosition(editor, model, caretPosition);
+    range.setStart(node, offset);
+    range.collapse(true);
+    sel.addRange(range);
+}
+
+function getNodeAndOffsetForPosition(editor, model, position) {
+    const {offset, lineIndex, nodeIndex} = getLineAndNodePosition(model, position);
     const lineNode = editor.childNodes[lineIndex];
 
     let focusNode;
@@ -35,9 +62,7 @@ export function setCaretPosition(editor, model, caretPosition) {
             focusNode = focusNode.firstChild;
         }
     }
-    range.setStart(focusNode, offset);
-    range.collapse(true);
-    sel.addRange(range);
+    return {node: focusNode, offset};
 }
 
 export function getLineAndNodePosition(model, caretPosition) {
diff --git a/src/editor/model.js b/src/editor/model.js
index 3b4f1ce460..ea6b05570c 100644
--- a/src/editor/model.js
+++ b/src/editor/model.js
@@ -433,7 +433,12 @@ export default class EditorModel {
      */
     transform(callback) {
         const pos = callback();
-        const acPromise = this._setActivePart(pos, true);
+        let acPromise = null;
+        if (!(pos instanceof Range)) {
+            acPromise = this._setActivePart(pos, true);
+        } else {
+            acPromise = Promise.resolve();
+        }
         this._updateCallback(pos);
         return acPromise;
     }