       ;; TODO: also restore scroll:
       ;;      scrollTop and scrollLeft from cm.getScrollerElement()
       ;;      source: https://groups.google.com/forum/#!topic/codemirror/oNzsevQW1DE
-      ))
+      ) 
+    )
   )
 
+;; NOTE:
+;; Text is either updated after a change in text or
+;; a cursor movement, but not both.
+;;
+;; When typing, on-change is called, then on-cursor-activity.
+;; So we prevent updating the text twice by using an update flag.
+
+(def frame-updated? (atom false))
+
+(defn on-change
+  "Called after typing something."
+  [cm change]
+  (update-text! cm)
+  (reset! frame-updated? true))
+
 (defn on-cursor-activity
+  "Called after the cursor moves."
   [cm]
-  (let [cursor (.getCursor cm)]
-    ;; TODO: trigger a change if we move lines, sending only the line. don't care about x-pos
-    )
-  )
+  (when-not @frame-updated?
+    (update-text! cm))
+  (reset! frame-updated? false))
 
 (def editor-opts
   {:lineNumbers "true"

;; Editor
;;------------------------------------------------------------------------

(defn replace-string-range
  [orig from to diff]
  (str
    (subs orig 0 from)
    diff
    (subs orig to)))

(defn calc-new-value
  "Calculate the new value of Code Mirror given this change.
  Might be able to ignore this"
  [cm change]
  (let [text (.getValue cm)
        no-lines (.lineCount cm)

        line-lengths (as-> text $
                          (split-lines $)
                          (map count $)
                          (concat $ (repeat 0))
                          (map inc $)
                          (take no-lines $))

        line-indexes (reduce
                         (fn [indexes length]
                           (let [prev-index (last indexes)
                                 new-index (+ prev-index length)]
                             (conj indexes new-index)))
                         [0]
                         line-lengths)

        diff (join "\n" (.-text change))

        get-marker (fn [end] {:x-pos (.-ch end) :line-no (.-line end)})
        markers [(get-marker (.-from change)) (get-marker (.-to change))]
        marker-sort-key (fn [x] [(:line-no x) (:x-pos x)])
        marker-to-index (fn [{:keys [line-no x-pos]}]
                          (+ x-pos (line-indexes line-no)))

        [from to] (->> markers
                       (sort-by marker-sort-key)
                       (map marker-to-index))

        new-text (replace-string-range text from to diff)]

    new-text))

(def target-text (atom ""))
(def global-cm nil)

(defn before-change
  [cm change]
  (println "BEFORE CHANGE")
  (let [new-text (calc-new-value cm change)]
    (println "new target:" (pr-str new-text))
    (println "current target:" (pr-str @target-text))

    (when-not (= new-text @target-text)
      (let [line-no (.. change -from -line)
            x-pos (.. change -from -ch)
            state {:cursor-line line-no
                   :cursor-x (inc x-pos)}
            formatted (format-text state new-text)]
        (reset! target-text formatted))))
  )

(defn on-change
  [cm change]
  (println "ON CHANGE")
  (let [cursor (.getCursor cm)
        current-text (.getValue cm)
        ]
    (println "current value:" (pr-str current-text))
    (println "current target:" (pr-str @target-text))
    (when-not (= current-text @target-text)

  (let [cm (.fromTextArea js/CodeMirror elm (clj->js editor-opts))]
    (set! global-cm cm)
    (.on cm "change" on-change)
    (.on cm "cursorActivity" on-cursor-activity)))

(defn root-comp
