Skip to content

Instantly share code, notes, and snippets.

@mrrodriguez
Last active June 30, 2024 16:13
Show Gist options
  • Save mrrodriguez/055fa3e5ab08d4c2e135a8ac14d11b3d to your computer and use it in GitHub Desktop.
Save mrrodriguez/055fa3e5ab08d4c2e135a8ac14d11b3d to your computer and use it in GitHub Desktop.
Simple Text Editor - Clojure
(ns metasimple.simple-editor.core
(:require
[clojure.string :as str]))
(defn- process-op-append
[{:keys [text] :as ed-state}
{:keys [op-type arg]}]
(-> ed-state
(update :text str arg)
(update :undo-history conj {:op-type op-type :text text})))
(defn undo-op-append
[ed-state
{:keys [text]}]
(assoc ed-state :text text))
(defn- process-op-delete
[{:keys [text] :as ed-state}
{:keys [op-type arg]}]
(-> ed-state
(update :text subs 0 arg)
(update :undo-history conj {:op-type op-type :text text})))
(defn undo-op-delete
[ed-state
{:keys [text]}]
(assoc ed-state :text text))
(defn- process-op-print
[ed-state {:keys [arg]}]
(println (get-in ed-state [:text (dec arg)]))
ed-state)
(declare op-type->exec-fns)
(defn- process-op-undo
[{:keys [undo-history] :as ed-state} _op]
(if-let [{:keys [op-type] :as undo-item} (peek undo-history)]
(let [{:keys [undo-fn]} (op-type->exec-fns op-type)]
(when-not undo-fn
(throw (ex-info (format "No `:undo-fn` found for undo item op: %s" undo-item)
{:undo-item undo-item})))
(-> ed-state
(update :undo-history pop)
(undo-fn undo-item)))
;; Nothing to undo.
ed-state))
(defn- parse-int
[^String s]
(Long/parseLong s))
(defn- create-op
[op-str]
(let [[op arg] (str/split op-str #" ")]
(case op
"1" {:op-type :append :arg arg}
"2" {:op-type :delete :arg (parse-int arg)}
"3" {:op-type :print :arg (parse-int arg)}
"4" {:op-type :undo}
(throw (ex-info (format "Unsupported op given: %s"
[op arg])
{:op op :arg arg :op-str op-str})))))
(def op-type->exec-fns
{:append {:process-fn process-op-append
:undo-fn undo-op-append}
:delete {:process-fn process-op-delete
:undo-fn undo-op-delete}
:print {:process-fn process-op-print}
:undo {:process-fn process-op-undo}})
(def ^:dynamic *with-tracing* false)
(defn- print-ed-state
[{:keys [text undo-history]}]
(println (format "History: %s text: '%s'" undo-history text)))
(defn- exec-op
[ed-state {:keys [op-type] :as op}]
(when *with-tracing*
(println "*********************************************")
(println (format "Processing op: %s" op))
(println "-- Before state:")
(print-ed-state ed-state)
(println "---------------"))
(let [{:keys [process-fn]} (op-type->exec-fns op-type)
new-ed-state (if process-fn
(process-fn ed-state op)
(throw (ex-info (format "No `:process-fn` found for op: %s" op)
{:op op})))]
(when *with-tracing*
(println "---------------")
(println "--- After state:")
(print-ed-state new-ed-state))
new-ed-state))
(defn process-input
[raw-ops]
(let [ops (mapv create-op raw-ops)
init-state {:text "" :undo-history []}]
(reduce exec-op
init-state
ops)))
(def input1
["1 abc"
"1 xyz"
"2 3"
"4"
"3 6"])
(comment
(do
(println "*********************************************")
(binding [*with-tracing* true] (process-input input1)))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment