(defn
create-editor
[paren-soup
content
events-chan
{:keys
[history-limit
append-limit
compiler-fn
console-callback
disable-clj?
edit-history],
:or {history-limit 100, append-limit 5000}}]
(let
[clj?
(not disable-clj?)
editor?
(not console-callback)
compiler-fn
(or compiler-fn (ir/create-compiler-fn))
edit-history
(doto
(or edit-history (mwm/create-edit-history))
(swap! assoc :limit history-limit))
refresh-instarepl-with-delay!
(debounce refresh-instarepl! 300)
console-history
(console/create-console-history)
last-highlight-elem
(atom nil)
allow-tab?
(atom false)]
(when-not
editor?
(set-validator!
edit-history
(fn
[{:keys [current-state states]}]
(if-let
[state (get states current-state)]
(->
state
:cursor-position
first
(>= (console/get-console-start console-history)))
true))))
(reify
Editor
(undo! [this] (some->> edit-history mwm/undo! (refresh! this)))
(redo! [this] (some->> edit-history mwm/redo! (refresh! this)))
(can-undo? [this] (mwm/can-undo? edit-history))
(can-redo? [this] (mwm/can-redo? edit-history))
(update-cursor-position!
[this position]
(try
(mwm/update-cursor-position! edit-history position)
(catch
js/Error
_
(when
(apply = position)
(let
[start (console/get-console-start console-history)]
(dom/set-cursor-position! content [start start])
(mwm/update-cursor-position! edit-history [start start])))))
(update-highlight! content last-highlight-elem))
(reset-edit-history!
[this start]
(console/update-console-start! console-history start)
(dom/set-cursor-position! content [start start])
(let
[new-edit-history
(mwm/create-edit-history)
state
{:cursor-position [start start], :text (.-textContent content)}]
(update-edit-history! new-edit-history state)
(reset! edit-history @new-edit-history)))
(append-text!
[this text]
(let
[node
(.createTextNode js/document text)
_
(.appendChild content node)
all-text
(.-textContent content)
char-count
(max 0 (- (count all-text) append-limit))
new-all-text
(subs all-text char-count)
char-count
(if
(.endsWith new-all-text "\n")
(dec (count new-all-text))
(count new-all-text))]
(when
(not= all-text new-all-text)
(set! (.-textContent content) new-all-text))
(reset-edit-history! this char-count)))
(enter!
[this]
(if
editor?
(.execCommand js/document "insertHTML" false "\n")
(let
[text
(trimr (.-textContent content))
post-text
(subs text (console/get-console-start console-history))]
(reset-edit-history! this (count text))
(console/update-console-history! console-history post-text)
(console-callback post-text))))
(up!
[this]
(when-not
editor?
(let
[text
(.-textContent content)
pre-text
(subs text 0 (console/get-console-start console-history))
line
(or (console/up! console-history) "")
state
{:cursor-position (dom/get-cursor-position content false),
:text (str pre-text line \newline)}]
(->>
state
(update-edit-history! edit-history)
(refresh! this)))))
(down!
[this]
(when-not
editor?
(let
[text
(.-textContent content)
pre-text
(subs text 0 (console/get-console-start console-history))
line
(or (console/down! console-history) "")
state
{:cursor-position (dom/get-cursor-position content false),
:text (str pre-text line \newline)}]
(->>
state
(update-edit-history! edit-history)
(refresh! this)))))
(tab! [this] (when editor? (reset! allow-tab? true)))
(refresh!
[this state]
(post-refresh-content!
content
events-chan
(if
editor?
(refresh-content! content state)
(refresh-console-content!
content
state
(console/get-console-start console-history)
clj?)))
(when
editor?
(some->
(.querySelector paren-soup ".numbers")
(refresh-numbers! (count (re-seq #"\n" (:text state)))))
(when
clj?
(when-let
[elem (.querySelector paren-soup ".instarepl")]
(when-not
(-> elem .-style .-display (= "none"))
(refresh-instarepl-with-delay! elem content compiler-fn)))))
(update-highlight! content last-highlight-elem))
(edit-and-refresh!
[this state]
(->>
state
(add-newline)
(add-parinfer clj? (console/get-console-start console-history))
(update-edit-history! edit-history)
(refresh! this)))
(initialize!
[this]
(when
editor?
(->> (init-state content false false) (edit-and-refresh! this))))
(refresh-after-key-event!
[this event]
(let
[tab?
(key-name? event :tab)
state
(init-state content editor? tab?)]
(when-not
(and tab? (not @allow-tab?))
(edit-and-refresh!
this
(case
(key-code event)
13
(assoc state :indent-type :return)
9
(assoc
state
:indent-type
(if (.-shiftKey event) :back :forward))
(assoc state :indent-type :normal))))
(when tab? (reset! allow-tab? false))))
(refresh-after-cut-paste!
[this]
(let
[html
(.-innerHTML content)
insert-newlines?
(-> html (.indexOf "</tr>") (> 0))
crop?
(and editor? (not insert-newlines?))]
(when
insert-newlines?
(set! (.-innerHTML content) (replace html "</tr>" \newline)))
(edit-and-refresh!
this
(assoc (init-state content crop? false) :indent-type :normal))))
(eval!
[this form callback]
(compiler-fn
[form]
(fn* [p1__18520#] (callback (first p1__18520#))))))))