         res))))
 
 (comment (
+  ;; regex playground!
   (. (js/RegExp. (.-source #"-?\d*\.?\d+") (str "g")) exec "1 2 3")
   (. (js/RegExp. (.-source #"h") (str "g")) exec "this is h h")))
 

  (filter (fn [x]
            (let [[match-pos match-str] x
                  end-pos (+ match-pos (count match-str))]
              (comment (print (str "match-pos: " match-pos ", "
                           "target-pos: " target-pos ", "
                           "end-pos: " end-pos)))
              (and (<= match-pos target-pos)
                       (>= end-pos target-pos))))
          (re-pos regex string)))

(get-next-value "0.00" 1)
(get-next-value "0.00" -1)

;; This holds the global state for this plugin
(def app-state (atom {:last-range 0
                      :origin ""}))

;; This is where the magic happens. We update the selected value
;; based on whether we are handling a continuous mouse scrub or
;; a single keyboard nudge. Most of the logic is shared, except
;; we need to dynamically add and remove mousemove/mouseup handlers.
(defn handle-keyboard-or-mouse-scrub [{:keys [type delta e]}]
  (if (or
       (= type :keyboard)
       ;; Alt-mouse click on window/linux, command-mouse on mac
       (and (= type :mouse) (or (.-metaKey e) (.-altKey e))))
      (let [ed (pool/last-active)
            cm (editor/->cm-ed ed)

            ;; We pick out the range of text that matches the regex
            ;; based on the position of the mouse or the keyboard cursor.
            pos  (condp = type
                       :mouse (.. cm (coordsChar #js {:left (.-pageX e) :top (.-pageY e)}))
                       :keyboard (.getCursor cm))
            line-text (editor/line ed (.-line pos))
            value-to-scrub (parse-for-scrub line-text (.-ch pos))
            [scrub-pos scrub-value] value-to-scrub
            last-range {:start {:line (.-line pos)
                                :ch scrub-pos}
                        :end {:line (.-line pos)
                              :ch (+ scrub-pos (count (str scrub-value)))}}

            last-text scrub-value

            ;; This is a unique string for CodeMirror's replaceRange, so that
            ;; we can undo to before the beginning of a scrub action
            origin (str "*scrubber" (:origin (swap! app-state update-in [:origin] inc)))

            ;; This method performs the scrub after setting up the mouse or
            ;; keyboard specific logic
            do-scrub! (fn [val-delta]
                        (let [new-text (get-next-value scrub-value val-delta)
                              last-range (get-in @app-state [:last-range])]
                          (if (not= new-text last-text)
                            (do
                              (.replaceRange cm new-text
                                             (clj->js (:start last-range))
                                             (clj->js (:end last-range))
                                             origin)
                              (swap! app-state update-in [:last-range :end :ch]
                                     #(+ (get-in last-range [:start :ch]) (count new-text)))
                              (editor/set-selection ed pos) ;; temporarily deselect text, so entire form evals
                              (cmd/exec! :eval-editor-form) ;; TODO: is there anyway to speed this up?
                              (editor/set-selection ed (:start last-range) (:end last-range)) ;; reselect text
                              ))))]

        ;; Update the state with this initial selected range before scrubbing
        (swap! app-state update-in [:last-range] (fn [] last-range))
        (editor/set-selection ed (:start last-range) (:end last-range)) ;; select the matched value

        ;; Input type specific handling
        (condp = type
          :mouse (let [scroller (.getScrollerElement cm)
                       down-x (.-pageX e)

                       ;; We want the mouse cursor to be a resizer, but to reset it
                       ;; to whatever it used to be after the handler is removed.
                       set-css-cursor (fn [cursors]
                                        (let [[val-1 val-2 val-3] cursors]
                                          (dom/css (dom/$ ".CodeMirror-lines") {:cursor val-1})
                                          (dom/css (dom/$ ".CodeMirror-gutter-elt") {:cursor val-2})
                                          (dom/css (dom/$ "html") {:cursor val-3})))
                       old-css-cursors [(dom/css (dom/$ ".CodeMirror-lines") :cursor)
                                        (dom/css (dom/$ ".CodeMirror-gutter-elt") :cursor)
                                        (dom/css (dom/$ "html") :cursor)]

                       ;; Scrub based on how far the mouse moves left or right from select value
                       move-handler (fn [e]
                                      (. e (stopPropagation))
                                      (. e (preventDefault))
                                      (let [px-delta (- (.-pageX e) down-x)
                                            val-delta (bit-or (/ px-delta 8) 0)]
                                        (do-scrub! val-delta)))]

                   (set-css-cursor (repeat 3 "col-resize"))

                   ;; Add the mouse handlers temporarily until mouse click is released
                   (.. window/document (addEventListener "mousemove" move-handler))
                   (.. window/document (addEventListener "mouseup"
                     (fn [e]
                       (.. window/document (removeEventListener "mousemove" move-handler))
                       (set-css-cursor old-css-cursors)))))

          :keyboard (do-scrub! delta)))))

;; We send the mouse-event to the scrub handler method for processing
(defn mouse-down-fn [e]
  (handle-keyboard-or-mouse-scrub {:type :mouse
                                   :e e}))

;; We also support nudging a value by a small amount using the keyboard
;; dir will be 1 for incrementing and -1 for decrementing
(defn nudge [dir]
  (handle-keyboard-or-mouse-scrub {:type :keyboard
                                   :delta dir}))

;; This Light Table object template contains whether the
;; scrubber is active or not.
