diff --git a/src/editor/caret.js b/src/editor/caret.js
index f93e9604d5..c56022d8c6 100644
--- a/src/editor/caret.js
+++ b/src/editor/caret.js
@@ -15,50 +15,104 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+import {needsCaretNodeBefore, needsCaretNodeAfter} from "./render";
+
 export function setCaretPosition(editor, model, caretPosition) {
     const sel = document.getSelection();
     sel.removeAllRanges();
     const range = document.createRange();
+    const {offset, lineIndex, nodeIndex} = getLineAndNodePosition(model, caretPosition);
+    const lineNode = editor.childNodes[lineIndex];
+
+    let focusNode;
+    // empty line with just a <br>
+    if (nodeIndex === -1) {
+        focusNode = lineNode;
+    } else {
+        focusNode = lineNode.childNodes[nodeIndex];
+        // make sure we have a text node
+        if (focusNode.nodeType === Node.ELEMENT_NODE && focusNode.firstChild) {
+            focusNode = focusNode.firstChild;
+        }
+    }
+    range.setStart(focusNode, offset);
+    range.collapse(true);
+    sel.addRange(range);
+}
+
+function getLineAndNodePosition(model, caretPosition) {
     const {parts} = model;
-    const {index} = caretPosition;
+    const partIndex = caretPosition.index;
+    const lineResult = findNodeInLineForPart(parts, partIndex);
+    const {lineIndex} = lineResult;
+    let {nodeIndex} = lineResult;
     let {offset} = caretPosition;
+    // we're at an empty line between a newline part
+    // and another newline part or end/start of parts.
+    // set offset to 0 so it gets set to the <br> inside the line container
+    if (nodeIndex === -1) {
+        offset = 0;
+    } else {
+        // move caret out of uneditable part (into caret node, or empty line br) if needed
+        ({nodeIndex, offset} = moveOutOfUneditablePart(parts, partIndex, nodeIndex, offset));
+    }
+    return {lineIndex, nodeIndex, offset};
+}
+
+function findNodeInLineForPart(parts, partIndex) {
     let lineIndex = 0;
     let nodeIndex = -1;
-    for (let i = 0; i <= index; ++i) {
+
+    let prevPart = null;
+    // go through to parts up till (and including) the index
+    // to find newline parts
+    for (let i = 0; i <= partIndex; ++i) {
         const part = parts[i];
-        if (part && part.type === "newline") {
-            if (i < index) {
-                lineIndex += 1;
-                nodeIndex = -1;
-            } else {
-                // if index points at a newline part,
-                // put the caret at the end of the previous part
-                // so it stays on the same line
-                const prevPart = parts[i - 1];
-                offset = prevPart ? prevPart.text.length : 0;
+        if (part.type === "newline") {
+            lineIndex += 1;
+            nodeIndex = -1;
+            prevPart = null;
+        } else {
+            nodeIndex += 1;
+            if (needsCaretNodeBefore(part, prevPart)) {
+                nodeIndex += 1;
+            }
+            // only jump over caret node if we're not at our destination node already,
+            // as we'll assume in moveOutOfUneditablePart that nodeIndex
+            // refers to the node  corresponding to the part,
+            // and not an adjacent caret node
+            if (i < partIndex) {
+                const nextPart = parts[i + 1];
+                const isLastOfLine = !nextPart || nextPart.type === "newline";
+                if (needsCaretNodeAfter(part, isLastOfLine)) {
+                    nodeIndex += 1;
+                }
+            }
+            prevPart = part;
+        }
+    }
+
+    return {lineIndex, nodeIndex};
+}
+
+function moveOutOfUneditablePart(parts, partIndex, nodeIndex, offset) {
+    // move caret before or after uneditable part
+    const part = parts[partIndex];
+    if (part && !part.canEdit) {
+        if (offset === 0) {
+            nodeIndex -= 1;
+            const prevPart = parts[partIndex - 1];
+            // if the previous node is a caret node, it's empty
+            // so the offset can stay at 0
+            // only when it's not, we need to set the offset
+            // at the end of the node
+            if (!needsCaretNodeBefore(part, prevPart)) {
+                offset = prevPart.text.length;
             }
         } else {
             nodeIndex += 1;
+            offset = 0;
         }
     }
-    let focusNode;
-    const lineNode = editor.childNodes[lineIndex];
-    if (lineNode) {
-        focusNode = lineNode.childNodes[nodeIndex];
-        if (!focusNode) {
-            focusNode = lineNode;
-        } else if (focusNode.nodeType === Node.ELEMENT_NODE) {
-            focusNode = focusNode.childNodes[0];
-        }
-    }
-    // node not found, set caret at end
-    if (!focusNode) {
-        range.selectNodeContents(editor);
-        range.collapse(false);
-    } else {
-        // make sure we have a text node
-        range.setStart(focusNode, offset);
-        range.collapse(true);
-    }
-    sel.addRange(range);
+    return {nodeIndex, offset};
 }