diff --git a/README.md b/README.md index 33f9fa4..f535d3a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - Docy shows dynamically customizable documentation for clojure namespaces. - Generated Documentation can be shown statically (say on github pages) - or it can be run interactively, which allows evaluating discovered examples. - +- snippets (with fully functional floating repl) can be linked to any function. # demo diff --git a/demo/deps.edn b/demo/deps.edn index 50b25cb..7bec166 100644 --- a/demo/deps.edn +++ b/demo/deps.edn @@ -3,7 +3,8 @@ ] :deps {org.clojure/clojure {:mvn/version "1.11.3"} - org.pinkgorilla/goldly-docs {:mvn/version "0.8.34"} + ;org.pinkgorilla/goldly-docs {:mvn/version "0.8.34"} + org.pinkgorilla/goldly-docs {:mvn/version "0.8.37"} io.github.pink-gorilla/docy {:local/root ".." :deps/manifest :deps} ; libs we want to document scicloj/tablecloth {:mvn/version "7.021"} ; brings techml-dataset diff --git a/demo/resources/ext/demo.edn b/demo/resources/ext/demo.edn new file mode 100644 index 0000000..6019485 --- /dev/null +++ b/demo/resources/ext/demo.edn @@ -0,0 +1,8 @@ +{:name "demo-docy" + :lazy true + :cljs-routes {"" reval.page.viewer/viewer-page + ; "devtools/repl" :repl + "frepl" demo.page.snippets/snippets-page} + +; + } \ No newline at end of file diff --git a/demo/src/config.edn b/demo/src/config.edn index 8a142dc..ef2be99 100644 --- a/demo/src/config.edn +++ b/demo/src/config.edn @@ -27,8 +27,21 @@ {:kb "ctrl-shift-enter" :handler [:notebook/evaluate-all] :desc "Evaluate all segments"} {:kb "ctrl-enter" :handler [:repl/eval-expression] :desc "Evaluate the highlighted segment"}] -:docy [; external - tablecloth.api - ] - +:docy {:namespaces [; external + tablecloth.api] + :snippets + [{:ns "demo.notebook.movies" + :kernel :clj + :label "print table (simple)" + :fns ["tablecloth.api/aggregate" + "tablecloth.api/columns"]} + {:ns "demo.notebook.simple" + :kernel :clj + :label "range" + :fns ["tablecloth.api/aggregate" + "tablecloth.api/columns" + "tablecloth.api/concat"] + }]} + +; } \ No newline at end of file diff --git a/demo/src/demo/dev.clj b/demo/src/demo/dev.clj new file mode 100644 index 0000000..e192a25 --- /dev/null +++ b/demo/src/demo/dev.clj @@ -0,0 +1,18 @@ +(ns demo.dev + (:require + [clojure.string :as string] + [modular.system] + [docy.core :refer [docy-data]])) + + ;; first lets get the running reval instance +(def s (modular.system/system :docy)) + + + +(def data + (docy-data s) + ) + +data + +(get-in data [:snippet-dict "tablecloth.api/concat"]) diff --git a/demo/src/demo/notebook/movies.clj b/demo/src/demo/notebook/movies.clj new file mode 100644 index 0000000..e2933ef --- /dev/null +++ b/demo/src/demo/notebook/movies.clj @@ -0,0 +1,26 @@ +(ns demo.notebook.movies + (:require + [clojure.pprint :refer [print-table]])) + +(println "hello\nworld!") + +(defn add3 [v] + (+ 3 v)) + +(def movies + [{:name "Terminator" :year 1984 :studio "Hemdale" :producer "Gale Anne Hurd"} + {:name "Men in Black" :year 1997 :studio "Amblin Entertainment" :producer "Walter F. Parkes"} + {:name "Matrix" :year 1999 :studio "Warner Bros" :producer "Joel Silver"} + {:name "Dr. Strange" :year 2016 :studio "Marvel Studios" :producer "Scott Derrickson"}]) + +*ns* + +(print-table movies) + + +(def more-movies + (conj movies {:name "Harry Potter" :year 2020})) + +(count more-movies) + + diff --git a/demo/src/demo/notebook/simple.clj b/demo/src/demo/notebook/simple.clj new file mode 100644 index 0000000..db6630e --- /dev/null +++ b/demo/src/demo/notebook/simple.clj @@ -0,0 +1,4 @@ +(ns demo.notebook.simple) + + +(range 20) \ No newline at end of file diff --git a/demo/src/demo/page/snippets.cljs b/demo/src/demo/page/snippets.cljs new file mode 100644 index 0000000..4bf650b --- /dev/null +++ b/demo/src/demo/page/snippets.cljs @@ -0,0 +1,42 @@ +(ns demo.page.snippets + (:require + [reval.frepl :refer [show-floating-repl show-floating-repl-namespace]] + [docy.snippet :refer [snippet-list]])) + + +(def snippets + [{:ns "demo.notebook.movies" + :kernel :clj + :label "print table (simple)"} + {:ns "demo.notebook.simple" + :kernel :clj + :label "range"}]) + + +(defn snippets-page [{:keys [route-params query-params handler] :as route}] + [:div + [:h1 "I am a normal reagent page. But I can add a floating repl."] + + ;; just code + [:a {:on-click #(show-floating-repl {:code "(+ 1 2 3)"})} + [:p "show code (floating)"]] + + ;; code with a example result saved. + [:a {:on-click #(show-floating-repl {:code "(+ 1 2 3)" + :render-fn 'reval.viz.render-fn/reagent + :data ^{:hiccup true} + [:span {:style {:color "blue"}} "25"]})} + [:p "show code (eval result)"]] + + ;; load a namespace + [:a {:on-click #(show-floating-repl-namespace {:ns "notebook.study.movies" + :kernel :clj})} + [:p "show code (namespace)"]] + + + [:a {:on-click #(show-floating-repl-namespace {:ns "demo.notebook.highcharts" + :kernel :clj})} + [:p "show code (highcharts)"]] + + [:h1 "now as a list:"] + [snippet-list snippets]]) diff --git a/demo/src/services.edn b/demo/src/services.edn index d6bae11..87b60e9 100644 --- a/demo/src/services.edn +++ b/demo/src/services.edn @@ -38,7 +38,9 @@ {:exts (clip/ref :exts) :clj (clip/ref :clj-service) :role nil - :namespaces (:docy (deref (clip/ref :config)))})} + :namespaces (:namespaces (:docy (deref (clip/ref :config)))) + :snippets (:snippets (:docy (deref (clip/ref :config)))) + })} diff --git a/src/docy/core.clj b/src/docy/core.clj index 6bab3bf..7422abf 100644 --- a/src/docy/core.clj +++ b/src/docy/core.clj @@ -1,88 +1,36 @@ (ns docy.core (:require - [orchard.info :as orchard] [taoensso.timbre :refer [info warn error]] [extension :as ext] - [clj-service.core :refer [expose-functions]])) - -(defn docstring [symbol] - (:doc (meta (resolve symbol)))) - -(defn describe-fun [nss fun] - (let [data (orchard/info nss fun)] - (merge - data - {:ns (str (:ns data)) - :name (str (:name data))}))) - - -(defn describe-ns [nss] - (require [nss]) - (let [symbols (keys (ns-publics nss))] - {:ns (str nss) - :names (->> symbols - (map #(describe-fun nss %)) - (filter :doc) - (remove :deprecated ) - (sort-by :name) - )})) - -(defn build-namespaces [ns-symbol-seq] - (info "building namespaces: " (count ns-symbol-seq)) - (let [r (->> (map describe-ns ns-symbol-seq) - (sort-by :ns) - ;(take 1) - (into []))] - ;(info "result: " r) - r)) - -(defn start-docy [{:keys [exts clj role namespaces]}] + [clj-service.core :refer [expose-functions]] + [docy.namespace :refer [build-namespaces ns-seq->dict]] + [docy.snippet :refer [build-fn-lookup]])) + +(defn docy-data [{:keys [namespaces snippets]}] + (let [ns-seq (build-namespaces namespaces) + ns-dict (ns-seq->dict ns-seq) + snippet-dict (build-fn-lookup snippets)] + {:ns-dict ns-dict + :snippet-dict snippet-dict})) + +(defn start-docy [{:keys [exts clj role namespaces snippets]}] (info "starting docy .. ") (assert (vector? namespaces)) - (info "starting docy namespaces: " (count namespaces)) + (assert (vector? snippets)) + (info "starting docy namespaces: " (count namespaces) + " snippets: " (count snippets)) ;(add-discovered-namespaces this exts) (if clj (do (info "starting docy clj-services..") (expose-functions clj {:name "docy" - :symbols ['docy.core/build-namespaces] + :symbols ['docy.core/docy-data] :permission role - :fixed-args [namespaces]})) + :fixed-args [{:namespaces namespaces + :snippets snippets}]})) (warn "docy starting without clj-services, perhaps you want to pass :clj key")) (info "docy running!") - namespaces) - -(comment - (orchard/info 'missionary.core 'amb>) - (describe-fun 'missionary.core 'amb>) - - ; deprecated should be removed - ; so amb and amb= should exist, but amb> should not - (->> (describe-ns 'missionary.core) - :names - (map :name)) - - (->> (describe-ns 'missionary.core) - :names - (filter #(= "amb" (:name %))) - ) - - - (describe-ns 'ta.calendar.core) - - (describe-ns 'ta.indicator.band) - - - (require '[modular.system]) - - (def d (modular.system/system :docy)) - - d - - (build-namespaces d) + {:namespaces namespaces + :snippets snippets}) - - - ; - ) \ No newline at end of file diff --git a/src/docy/namespace.clj b/src/docy/namespace.clj new file mode 100644 index 0000000..ccab913 --- /dev/null +++ b/src/docy/namespace.clj @@ -0,0 +1,68 @@ +(ns docy.namespace + (:require + [orchard.info :as orchard] + [taoensso.timbre :refer [info warn error]])) + +(defn docstring [symbol] + (:doc (meta (resolve symbol)))) + +(defn describe-fun [nss fun] + (let [data (orchard/info nss fun)] + (merge + data + {:ns (str (:ns data)) + :name (str (:name data))}))) + +(defn describe-ns [nss] + (require [nss]) + (let [symbols (keys (ns-publics nss))] + {:ns (str nss) + :names (->> symbols + (map #(describe-fun nss %)) + (filter :doc) + (remove :deprecated) + (sort-by :name))})) + +(defn build-namespaces [ns-symbol-seq] + (info "building namespaces: " (count ns-symbol-seq)) + (let [r (->> (map describe-ns ns-symbol-seq) + (sort-by :ns) + ;(take 1) + (into []))] + ;(info "result: " r) + r)) + +(defn ns-seq->dict [ns-seq] + (->> ns-seq + (map (fn [{:keys [ns names]}] + [ns names])) + (into {}))) + +(comment + (orchard/info 'missionary.core 'amb>) + (describe-fun 'missionary.core 'amb>) + + ; deprecated should be removed + ; so amb and amb= should exist, but amb> should not + (->> (describe-ns 'missionary.core) + :names + (map :name)) + + (->> (describe-ns 'missionary.core) + :names + (filter #(= "amb" (:name %)))) + + (describe-ns 'ta.calendar.core) + + (describe-ns 'ta.indicator.band) + + (require '[modular.system]) + + (def d (modular.system/system :docy)) + + d + + (build-namespaces d) + +; + ) \ No newline at end of file diff --git a/src/docy/page.cljs b/src/docy/page.cljs index 247d441..143ca5c 100644 --- a/src/docy/page.cljs +++ b/src/docy/page.cljs @@ -4,30 +4,29 @@ [promesa.core :as p] [frontend.notification :refer [show-notification]] [goldly.service.core :refer [clj]] - [docy.util :refer [link text]])) + [docy.util :refer [link text]] + [docy.snippet :refer [snippet-list]])) (def namespaces-dict-a (r/atom {})) -(defn ns-seq->dict [ns-seq] - (->> ns-seq - (map (fn [{:keys [ns names]}] - [ns names])) - (into {}))) +(def snippet-dict-a (r/atom {})) (defn ^:export init-docy! - [new-namespaces] - (println "init-docy: " new-namespaces) - (println "init-docy! ns-count: " (count new-namespaces)) - (reset! namespaces-dict-a (ns-seq->dict new-namespaces))) + [ns-dict snippet-dict] + (println "init-docy: " ns-dict) + (println "init-docy! ns-count: " (count ns-dict)) + (println "snippet dict: " snippet-dict) + (reset! namespaces-dict-a ns-dict) + (reset! snippet-dict-a snippet-dict)) (defn get-data [] - (let [rp (clj 'docy.core/build-namespaces)] + (let [rp (clj 'docy.core/docy-data)] (-> rp - (p/then (fn [ns-seq] - (init-docy! ns-seq))) + (p/then (fn [{:keys [ns-dict snippet-dict]}] + (init-docy! ns-dict snippet-dict))) (p/catch (fn [err] - (println "docy fetch ns-seq error: " err) - (show-notification :error "docy ns-seq fetch failed!")))))) + (println "docy data fetch error: " err) + (show-notification :error "docy data fetch failed!")))))) ; this makes sense because it only needs to be done once. (get-data) @@ -49,22 +48,27 @@ ; :line 496, ; :column 1} - -(defn fun-ui [{:keys [name doc file line column arglists macro] :as fun}] - [:<> +(defn fun-ui [{:keys [ns name doc file line column arglists macro] :as fun}] + (let [fqfn (str ns "/" name) + snippets (get @snippet-dict-a fqfn)] + (println "fqfn: " fqfn) + [:<> ; colum left - function name - [:h1.text-xxl.text-blue-800.m-1.p-1 - name] + [:h1.text-xxl.text-blue-800.m-1.p-1 + name] ; column right - arglist and docstring - [:div {:class "bg-gray-300 border-solid border-green-300" - :style {:max-height "4cm" - :overflow "auto"}} - (when arglists - [:span {:class "text-blue-900 text-bold"} - (str arglists)]) - (when macro - [:span {:class "text-red-500 p-1"} "macro"]) - [text doc]]]) + [:div {:class "bg-gray-300 border-solid border-green-300" + :style {:max-height "4cm" + :overflow "auto"}} + (when arglists + [:span {:class "text-blue-900 text-bold"} + (str arglists)]) + (when macro + [:span {:class "text-red-500 p-1"} "macro"]) + [text doc] + [snippet-list snippets] + ;[:span (pr-str snippets)] + ]])) (defn docy-ns-page [{:keys [route-params] :as route}] (fn [{:keys [route-params] :as route}] @@ -84,8 +88,7 @@ [link {:to ['docy.page/docy-ns-page :nss (str ns-name)]} [:span {:style {:width "100px" :min-width "100px"} - :class "w-64" - } + :class "w-64"} (str ns-name)]]]) (defn ns-list [ns-symbol-seq] diff --git a/src/docy/snippet.clj b/src/docy/snippet.clj new file mode 100644 index 0000000..c244c47 --- /dev/null +++ b/src/docy/snippet.clj @@ -0,0 +1,41 @@ +(ns docy.snippet) + +(defn add-fn [snippet dict fn] + (if (get dict fn) + (update-in dict [fn] conj snippet) + (assoc dict fn [snippet]))) + +(defn add-snippet [dict {:keys [fns] :as snippet}] + (reduce (partial add-fn snippet) dict fns)) + +(defn build-fn-lookup [snippets] + (reduce add-snippet {} snippets)) + +(comment + + (def demo-snippets + [{:ns "demo.notebook.movies" + :kernel :clj + :label "print table (simple)" + :fns ["tablecloth.api/aggregate" + "tablecloth.api/columns"]} + {:ns "demo.notebook.simple" + :kernel :clj + :label "range" + :fns ["tablecloth.api/aggregate" + "tablecloth.api/columns" + "tablecloth.api/concat" + "tablecloth.api/dataset"]}]) + + (add-snippet {} + {:ns "demo.notebook.movies" + :kernel :clj + :label "print table (simple)" + :fns ["tablecloth.api/aggregate" + "tablecloth.api/columns"]}) + + (def d (build-fn-lookup demo-snippets)) + + (keys d) +; + ) diff --git a/src/docy/snippet.cljs b/src/docy/snippet.cljs new file mode 100644 index 0000000..27f51a4 --- /dev/null +++ b/src/docy/snippet.cljs @@ -0,0 +1,16 @@ +(ns docy.snippet + (:require + [reval.frepl :refer [show-floating-repl show-floating-repl-namespace]])) + +(defn snippet-item [{:keys [ns kernel label]}] + [:a {:on-click #(show-floating-repl-namespace {:ns ns + :kernel kernel})} + [:span.pr-1.text-blue-500 label]]) + +(defn snippet-list [snippets] + (if snippets + (into [:div] + (map snippet-item snippets)) + [:div.text-blue-500 "no snippets"])) + + diff --git a/src/docy/util.cljs b/src/docy/util.cljs index 125fddd..4bc64b3 100644 --- a/src/docy/util.cljs +++ b/src/docy/util.cljs @@ -17,7 +17,6 @@ (into [:a {:class class :style style :on-click goto-link}] body))) - (defn line-with-br [t] [:div @@ -30,5 +29,5 @@ Needed because \\n is meaningless in html" [t] (let [lines (str/split t #"\n")] - (into [:div ] (map line-with-br lines)))) + (into [:div] (map line-with-br lines))))