1
votes

I am trying to make a simple text editor in clojure just to get familiar with it. I was thinking of using zippers for the structure and navigating and updating the editor.

I was thinking of storing the text of the editor in a document something like:

(def document-test {
:id "doc1"
:info {
    :title "Moluctular bio"
    :description "Test document"
}
:nodes [{
        :type "header"
        :content "This is a header"
        :id "h1"
    }
    {
        :type "p"
        :content "this is a paragraph"
        :id "p1"
    }
    {
        :type "todo"
        :content "Todo list"
        :id "t1"
        :nodes [
            {
                :type "todoelement"
                :content "Do this"
                :id "td1"
            }
            {
                :type "todoelement"
                :content "Do that"
                :id "td2"
            }]}]})

So I want to make a zipper that would be able to easily navigate this. Maybe a zipper would not be the best for this? But I was thinking of starting in the root. Going down would take you to the nodes of that children. So the top of the document is id h1.

I have the following, but it does not allow for the children to be an array:

(defn map-zip [m] 
  (zip/zipper 
    #(map? (second %)) 
        #(seq (second %)) 
       (fn [node children] [(first node) (into {} children)]) 
       [:root m])) 

It's more expecting something like:

{:foo {:bar {:baz 2}}}

Any thoughts? I am open to changing the structure up completely if someone has any suggestions.

1

1 Answers

1
votes

You can build a zipper to navigate that structure like the following:

(def z (zip/zipper 
        (constantly true) ; a node can always have children
        :nodes ; child nodes in the :nodes key, and keywords are functions
        (fn [node children]
          (update-in node [:nodes] #(into (or % []) children)))
        document-test))

(-> z
    zip/down
    zip/right
    zip/right
    (zip/insert-child {:type "h3" :id "thing1" :content "It's a h3!"})
    zip/down
    zip/right
    zip/right
    zip/node)

You may also want to look at the parse tree format of Enlive or Instaparse for ideas about structure. Reusing their structure may give you some interoperability you don't get with rolling your own.