Add sci integration

pull/645/head
Tienson Qin 2020-06-11 16:31:42 +08:00
parent 32065a246c
commit 7b2835b983
5 changed files with 239 additions and 98 deletions

View File

@ -13,7 +13,9 @@
prismatic/dommy {:mvn/version "1.1.0"}
org.clojure/core.match {:mvn/version "1.0.0"}
com.andrewmcveigh/cljs-time {:mvn/version "0.5.2"}
cljs-drag-n-drop {:mvn/version "0.1.0"}}
cljs-drag-n-drop {:mvn/version "0.1.0"}
borkdude/sci {:mvn/version "0.0.13-alpha.27"}
}
:aliases {:cljs {:extra-paths ["src/dev-cljs/"]
:extra-deps {org.clojure/clojurescript {:mvn/version "1.10.520"}

View File

@ -17,7 +17,9 @@
[frontend.handler :as handler]
[goog.object :as gobj]
[medley.core :as medley]
[cljs.reader :as reader]))
[cljs.reader :as reader]
[frontend.extensions.sci :as sci]
["/frontend/utils" :as utils]))
;; local state
(defonce *mouse
@ -25,6 +27,10 @@
(defonce *dragging?
(atom false))
(defonce *dragging-heading
(atom nil))
(defonce *move-to-top?
(atom false))
;; TODO:
;; add `key`
@ -282,7 +288,7 @@
(->elem
:a
(cond->
{:href href}
{:href href}
title
(assoc :title title))
(map-inline config label))
@ -295,8 +301,8 @@
(->elem
:a
(cond->
{:href href
:target "_blank"}
{:href href
:target "_blank"}
title
(assoc :title title))
(map-inline config label))))))
@ -373,7 +379,7 @@
(defonce *control-show? (atom {}))
(rum/defc heading-control < rum/reactive
[config uuid heading-id level start-level collapsed? collapsed-atom? body children dummy?]
[config uuid heading-id level start-level collapsed? collapsed-atom? body children heading dummy?]
(let [control-show (util/react (rum/cursor *control-show? heading-id))
dark? (= "dark" (state/sub :ui/theme))
has-child? (or (seq children)
@ -406,23 +412,25 @@
[:span ""])]
[:a
(cond->
{:id (str "dot-" uuid)
:draggable true
:on-drag-start (fn [event]
(.setData (gobj/get event "dataTransfer")
"heading-uuid"
uuid)
(.setData (gobj/get event "dataTransfer")
"heading-dom-id"
heading-id)
(reset! *dragging? true))
;; :on-drag-end (fn [event]
;; (reset! *dragging? false)
;; (reset! *mouse {}))
{:id (str "dot-" uuid)
:draggable true
:on-drag-start (fn [event]
(handler/highlight-heading! uuid)
(.setData (gobj/get event "dataTransfer")
"heading-uuid"
uuid)
(.setData (gobj/get event "dataTransfer")
"heading-dom-id"
heading-id)
(reset! *dragging? true)
(reset! *dragging-heading heading))
;; :on-drag-end (fn [event]
;; (reset! *dragging? false)
;; (reset! *mouse {}))
:style {:width 16
:height 16}
:headingid (str uuid)}
:style {:width 16
:height 16}
:headingid (str uuid)}
(not dummy?)
(assoc :href (str "/page/" uuid)
:on-click (fn [e]
@ -465,17 +473,23 @@
:else nil))
(rum/defc dnd-separator
[heading margin-left top?]
(let [id (str (:heading/uuid heading))]
[heading margin-left bottom top? nested?]
(let [id (str (:heading/uuid heading)
(cond nested?
"-nested"
top?
"-top"
:else
nil))]
[:div.dnd-separator
{:id id
:style (merge
{:position "absolute"
:left 0
:left margin-left
:width (- 700 margin-left)}
(if top?
{:top 0}
{:bottom 0}))
{:bottom bottom}))
:on-mouse-move (fn [event]
(let [client-x (gobj/get event "clientX")]
(reset! *mouse {:client-x client-x})))}]))
@ -486,7 +500,7 @@
{:did-update (fn [state]
(util/code-highlight!)
state)}
[state {:heading/keys [uuid title level body meta content dummy? lock? page format repo children] :as heading} heading-part config]
[state {:heading/keys [uuid title level body meta content dummy? lock? page format repo children idx] :as heading} heading-part config]
(let [dragging? (rum/react *dragging?)
config (assoc config :heading heading)
ref? (boolean (:ref? config))
@ -504,7 +518,20 @@
has-child?)
@collapsed-atom?)
start-level (or (:start-level config) 1)
margin-left -27]
show-dnd-separator (fn [element-id]
(when-let [element (gdom/getElement element-id)]
(when (d/has-class? element "dnd-separator")
(d/remove-class! element "dnd-separator")
(d/add-class! element "dnd-separator-cur"))))
hide-dnd-separator (fn [element-id]
(when-let [element (gdom/getElement element-id)]
(when (d/has-class? element "dnd-separator-cur")
(d/remove-class! element "dnd-separator-cur")
(d/add-class! element "dnd-separator"))))
get-data-transfer-attr (fn [event attr]
(.getData (gobj/get event "dataTransfer") attr))
dnd-same-heading? (fn [event]
(= (:heading/uuid @*dragging-heading) uuid))]
(when-not lock?
[:div.ls-heading.flex.flex-col
{:id heading-id
@ -515,31 +542,37 @@
:repo repo
:level level
:on-drag-over (fn [event]
(let [element (gdom/getElement (str uuid))]
(when (d/has-class? element "dnd-separator")
(d/remove-class! element "dnd-separator")
(d/add-class! element "dnd-separator-cur")))
(util/stop event))
(util/stop event)
(when-not (dnd-same-heading? event)
(if (zero? idx)
(let [element-top (gobj/get (utils/getOffsetRect (gdom/getElement heading-id)) "top")
cursor-top (gobj/get event "clientY")]
(if (<= (js/Math.abs (- cursor-top element-top)) 16)
;; top
(do
(hide-dnd-separator (str uuid))
(show-dnd-separator (str uuid "-top"))
(reset! *move-to-top? true))
(do
(hide-dnd-separator (str uuid "-top"))
(show-dnd-separator (str uuid)))))
(show-dnd-separator (str uuid)))))
:on-drag-leave (fn [event]
(let [element (gdom/getElement (str uuid))]
(when (d/has-class? element "dnd-separator-cur")
(d/remove-class! element "dnd-separator-cur")
(d/add-class! element "dnd-separator"))))
(hide-dnd-separator (str uuid))
(hide-dnd-separator (str uuid "-nested"))
(hide-dnd-separator (str uuid "-top"))
(reset! *move-to-top? false))
:on-drop (fn [event]
(when-not (dnd-same-heading? event)
(let [from-dom-id (get-data-transfer-attr event "heading-dom-id")]
(handler/move-heading @*dragging-heading
heading
from-dom-id
@*move-to-top?
false)))
(reset! *dragging? false)
;; (let [get-data-transfer-attr (fn [attr] (.getData (gobj/get event "dataTransfer") attr))
;; from-id (cljs.core/uuid (get-data-transfer-attr "heading-uuid"))
;; from-dom-id (get-data-transfer-attr "heading-dom-id")]
;; (cond
;; (= from-id (:heading/uuid heading))
;; nil
;; :else
;; (handler/move-heading from-id
;; heading
;; from-dom-id
;; false)))
)
(reset! *dragging-heading nil)
(handler/unhighlight-heading!))
:on-mouse-over (fn [e]
(util/stop e)
(when has-child?
@ -549,9 +582,10 @@
(when has-child?
(swap! *control-show?
assoc heading-id false)))}
[:div.flex-1.flex-row
{:style {:margin-left margin-left}}
(heading-control config uuid heading-id level start-level collapsed? collapsed-atom? body children dummy?)
(when (and (zero? idx) dragging?)
(dnd-separator heading 30 0 true false))
[:div.flex-1.flex-row.py-1
(heading-control config uuid heading-id level start-level collapsed? collapsed-atom? body children heading dummy?)
(if edit?
(editor/box (string/trim content)
@ -564,7 +598,7 @@
(when (= event :esc)
(handler/highlight-heading! uuid)))}
edit-input-id)
[:div.flex.flex-col
[:div.flex.flex-col.relative
{:style {:cursor "text"
:min-height 24}
:on-click (fn [e]
@ -580,9 +614,32 @@
(state/set-editing!
edit-input-id
(handler/remove-level-spaces content format)
heading))))}
heading))))
:on-drag-over (fn [event]
(util/stop event)
(when-not (dnd-same-heading? event)
(show-dnd-separator (str uuid "-nested"))))
:on-drag-leave (fn [event]
(hide-dnd-separator (str uuid))
(hide-dnd-separator (str uuid "-nested"))
(hide-dnd-separator (str uuid "-top")))
:on-drop (fn [event]
(util/stop event)
(when-not (dnd-same-heading? event)
(let [from-dom-id (get-data-transfer-attr event "heading-dom-id")]
(handler/move-heading @*dragging-heading
heading
from-dom-id
false
true)))
(reset! *dragging? false)
(reset! *dragging-heading nil)
(handler/unhighlight-heading!))}
heading-part
(when dragging?
(dnd-separator heading 0 -4 false true))
(when (seq body)
[:div.heading-body
(for [child body]
@ -591,14 +648,12 @@
(cljs.core/random-uuid))))])])]
(when (seq children)
[:div.heading-children {:style {:padding-left 20
:margin-left 6}}
[:div.heading-children {:style {:margin-left 33}}
(for [child children]
(rum/with-key (heading-container config child)
(:heading/uuid child)))])
(when dragging?
(dnd-separator heading 0 false))
])))
(dnd-separator heading 30 0 false))])))
(rum/defc heading-checkbox
[heading class]
@ -621,7 +676,7 @@
nil))
(rum/defc heading-container < rum/static
[config {:heading/keys [uuid title tags marker level priority anchor meta format content]
[config {:heading/keys [uuid title tags marker level priority anchor meta format content idx]
:as t}]
(let [config (assoc config :heading t)
checkbox (heading-checkbox t (str "mr-1 cursor"))
@ -814,11 +869,20 @@
["Src" options]
(let [{:keys [language options lines]} options
attr (if language
{:data-lang language
:class lines})]
[:pre.pre-wrap-white-space.code
[:code attr
(join-lines lines)]])
{:data-lang language})
code (join-lines lines)]
(if (= language "clojure")
[:div
[:pre.pre-wrap-white-space.code
[:code attr code]]
(let [result (sci/eval-string code)]
[:div
[:code "Results:"]
[:div.results.mt-1
[:pre.pre-wrap-white-space.code
[:code attr (str result)]]]])]
[:pre.pre-wrap-white-space.code
[:code attr code]]))
["Quote" l]
(->elem
:blockquote
@ -886,14 +950,14 @@
(rum/defc headings-cp
[headings config]
[:div.headings-container
[:div.headings-container {:style {:margin-left -24}}
(let [headings (db/headings->vec-tree headings)]
(for [item headings]
(for [[idx item] (medley/indexed headings)]
(let [item (if (:heading/dummy? item)
item
(dissoc item :heading/meta))]
(rum/with-key
(heading-container config item)
(heading-container config (assoc item :heading/idx idx))
(:heading/uuid item)))))])
(defn ->hiccup

View File

@ -19,6 +19,7 @@
[promesa.core :as p]
[cljs.reader :as reader]
[cljs-time.core :as t]
[clojure.walk :as walk]
[frontend.util :as util :refer-macros [profile]]))
;; offline db
@ -1304,6 +1305,26 @@
other-children)]
(recur others children))))))))
;; recursively with children content
(defn get-heading-content-rec
[heading]
(let [contents (atom [])
_ (walk/postwalk
(fn [form]
(when (map? form)
(when-let [content (:heading/content form)]
(swap! contents conj content)))
form)
heading)]
(apply str (reverse @contents))))
(defn get-heading-end-pos-rec
[heading]
(let [children (:heading/children heading)]
(if (seq children)
(get-heading-end-pos-rec (last children))
(get-in heading [:heading/meta :end-pos]))))
(comment
(defn debug!
[]

View File

@ -0,0 +1,10 @@
(ns frontend.extensions.sci
(:require [sci.core :as sci]))
;; #+begin_src clojure :results
;; (+ 1 4)
;; #+end_src
;; TODO: lazy load extensions
(def eval-string sci/eval-string)

View File

@ -1049,34 +1049,83 @@
(string/join "\n" others)))))]
(save-heading-if-changed! heading new-content)))))
;; previous-sibling
;; top-part-of-to-target, insert-before-the-heading
;; 1. calculate the new `level`, compare the new level with the target level:
;; 2. if the level increased, nested to the target heading
;; 3. if the level not changed:
;; 1. if the target heading is the `previous sibling`, do nothing
;; 2. otherwise, down to the target heading
(defn move-heading
"There can be several possible situations:
"There can be two possible situations:
1. Move a heading in the same file (either top-to-bottom or bottom-to-top).
2. Move a heading between two different files.
"
[from-id to-heading from-dom-id top?]
(prn "move heading")
(let [heading (db/entity [:heading/uuid from-id])
from-content (string/replace (string/trim (:heading/content heading)) #"\*+\s" "")
to-content (string/replace (string/trim (:heading/content to-heading)) #"\*+\s" "")]
(if top?
(println "moving" from-content "to the top of" to-content)
(println "moving" from-content "to the bottom of" to-content)
[target-heading to-heading target-dom-id top? nested?]
(when (and
target-heading to-heading target-dom-id
(not= (:heading/uuid target-heading) (:heading/uuid to-heading)))
(let [target-heading (assoc target-heading
:heading/meta
(:heading/meta (db/entity [:heading/uuid (:heading/uuid target-heading)])))
to-heading (assoc to-heading
:heading/meta
(:heading/meta (db/entity [:heading/uuid (:heading/uuid to-heading)])))
same-file? (= (:db/id (:heading/file target-heading))
(:db/id (:heading/file to-heading)))
get-start-pos (fn [heading] (get-in heading [:heading/meta :pos]))
get-end-pos (fn [heading] (get-in heading [:heading/meta :end-pos]))
[top-heading bottom-heading] (if same-file?
(if (< (get-start-pos target-heading)
(get-start-pos to-heading))
[target-heading to-heading]
[to-heading target-heading])
[nil nil])
direction (if (= top-heading target-heading) :down :up)]
;; debug
(let [target-content (string/replace (string/trim (:heading/content target-heading)) #"\*+\s" "")
to-content (string/replace (string/trim (:heading/content to-heading)) #"\*+\s" "")]
(cond
top?
(println "moving" target-content "to the top of" to-content)
nested?
(println "moving" target-content "nested to" to-content)
:else
(println "moving" target-content "to the bottom of" to-content)))
(if same-file?
(let [old-file-content (-> (db/get-file (:file/path (db/entity (:db/id (:heading/file target-heading)))))
(utf8/encode))
subs (fn [start-pos end-pos] (utf8/substring old-file-content start-pos end-pos))
bottom-content (db/get-heading-content-rec bottom-heading)
top-content (db/get-heading-content-rec top-heading)
new-file-content (if nested?
(let [between-area (if (= direction :down)
(subs (db/get-heading-end-pos-rec target-heading) (get-start-pos to-heading))
(subs (db/get-heading-end-pos-rec to-heading) (get-start-pos target-heading)))]
(str
(subs 0 (get-start-pos top-heading))
(when (= direction :up)
(str (:heading/content top-heading)
bottom-content
(db/get-heading-content-rec (:heading/children top-heading))))
between-area
(when (= direction :down)
(str (:heading/content bottom-content)
top-content
(db/get-heading-content-rec (:heading/children bottom-content))))
;; after
(subs (db/get-heading-end-pos-rec bottom-heading) nil)))
;; (let [prev-heading (util/get-prev-heading (gdom/getElement from-dom-id))
;; prev-heading-id (and prev-heading
;; (some-> (dom/attr prev-heading "headingid")
;; (uuid)))]
;; (= to-id prev-heading-id))
)))
(let [between-area (if (= direction :down)
(subs (db/get-heading-end-pos-rec target-heading) (get-start-pos to-heading))
(subs (db/get-heading-end-pos-rec to-heading) (get-start-pos target-heading)))]
(str
(subs 0 (get-start-pos top-heading))
(when (= direction :up)
(if top?
(str bottom-content top-content)
(str top-content bottom-content)))
between-area
(when (= direction :down)
(str bottom-content top-content))
;; after
(subs (db/get-heading-end-pos-rec bottom-heading) nil))))]
(prn "New file content: ")
(println new-file-content)
)))))
(defn clone-and-pull
[repo-url]
@ -1224,13 +1273,8 @@
(defn watch-for-date!
[]
(state/set-today! (date/today))
(let [ms (-> (t/interval (t/now)
(t/plus (t/today) (t/days 1)))
(t/in-millis)
(+ 1000))]
(js/setTimeout (fn []
(state/set-today! (date/today))) ms)))
(js/setInterval #(state/set-today! (date/today))
1000))
(defn start!
[render]