                    (assoc :backup backup)
                    (update-in [:lines line-no] str ch))
 
+        insert-at-cursor? (and (not track-indent?)
+                               (= x-pos (dec cursor-x))
+                               (= line-no cursor-line)
+                               (in-code? stack)
+                               (whitespace? ch))
+
+        closing-delim? (isa? char-hierarchy ch :close)
+
+        insert-at-char? (and (not= "" ch)
+                             (not (whitespace? ch))
+                             (or (and (= line-no cursor-line)
+                                      closing-delim?)
+                                 (not closing-delim?))
+                             (in-code? stack))
+
         ;; Add potential insert point for closing delimiters if required.
-        insert (when (and (not= "" ch)
-
-                          ;; only allow whitespace as an insert if we have already found the indentation point
-                          ;; (i.e. a whitespace-only line)
-                          (or (and (not track-indent?) (whitespace? ch))
-                              (not (whitespace? ch)))
-                          (not (isa? char-hierarchy ch :close))
-                          (seq stack)
-                          (in-code? stack))
+        insert (when (or insert-at-cursor?
+                         insert-at-char?)
                  {:line-no line-no
                   :x-pos (inc x-pos)})
         result (cond-> result insert (assoc :insert insert))]


(defn process-char
  "Update the given result by processing the given character and its position."
  [{:keys [stack line-no track-indent?] :as result} [x-pos ch]]

  (let [result (process-delim-trail result [x-pos ch])
        backup (:backup result)

      (html
        [:div
         [:textarea
          {:on-change trigger-change!}]
         [:pre.computed (:full-text data)]]))))

;; hack to initialize the state on first load (not when reloading)
