From 24bb5322eb40b6fda038b451c09bab3ae0d5fb33 Mon Sep 17 00:00:00 2001 From: Konstantinos Kaloutas Date: Sun, 25 Feb 2024 14:14:33 +0200 Subject: [PATCH] refactor repl module --- src/renderer/attribute/views.cljs | 2 +- src/renderer/components.cljs | 17 +-- src/renderer/document/styles.css | 2 +- src/renderer/document/views.cljs | 4 +- src/renderer/menubar/views.cljs | 5 - src/renderer/reepl/README.md | 2 +- src/renderer/reepl/codemirror.cljs | 20 ++-- src/renderer/reepl/completions.cljs | 76 +++++-------- src/renderer/reepl/core.cljs | 148 ++++++++++---------------- src/renderer/reepl/handlers.cljs | 30 ++++-- src/renderer/reepl/helpers.cljs | 58 ---------- src/renderer/reepl/repl_items.cljs | 60 ++--------- src/renderer/reepl/replumb.cljs | 30 ++++-- src/renderer/reepl/show_devtools.cljs | 23 ++-- src/renderer/reepl/show_function.cljs | 42 ++++---- src/renderer/reepl/show_value.cljs | 9 +- src/renderer/reepl/styles.css | 10 +- src/renderer/reepl/subs.cljs | 6 +- src/renderer/reepl/views.cljs | 29 ++--- src/renderer/styles.css | 11 +- src/renderer/utils/dom.cljs | 3 +- src/renderer/views.cljs | 27 ++--- 22 files changed, 233 insertions(+), 381 deletions(-) delete mode 100644 src/renderer/reepl/helpers.cljs diff --git a/src/renderer/attribute/views.cljs b/src/renderer/attribute/views.cljs index f3545b96..e4eda8fb 100644 --- a/src/renderer/attribute/views.cljs +++ b/src/renderer/attribute/views.cljs @@ -84,7 +84,7 @@ :on-blur #(on-change-handler % key value) :on-key-down #(keyb/input-key-down-handler % value on-change-handler key value)}] (when-not (or (empty? (str value)) disabled?) - [:button.button.ml-px.level-1.text-muted.absolute.right-0.clear-input-button + [:button.button.ml-px.level-1.text-muted.absolute.right-0.clear-input-button.hover:bg-transparent {:style {:width "26px" :height "26px"} :on-pointer-down #(rf/dispatch [:element/remove-attr key])} [comp/icon "times" {:class "small"}]])]) diff --git a/src/renderer/components.cljs b/src/renderer/components.cljs index 44c0e7f0..bcf7623d 100644 --- a/src/renderer/components.cljs +++ b/src/renderer/components.cljs @@ -48,13 +48,16 @@ (into [:div.shortcuts]))))) (defn toggle-icon-button - [{:keys [active? active-icon inactive-icon active-text inactive-text action class]}] - [:button.icon-button {:class class - :title (if active? active-text inactive-text) - :on-double-click #(.stopPropagation %) - :on-click #(when action - (.stopPropagation %) - (action))} + [{:keys [active? active-icon inactive-icon active-text inactive-text action class]} + attrs] + [:button.icon-button + (merge attrs + {:class class + :title (if active? active-text inactive-text) + :on-double-click #(.stopPropagation %) + :on-click #(when action + (.stopPropagation %) + (action))}) [icon (if active? active-icon inactive-icon) {:fixed-width true}]]) (defn toggle-collapsed-button diff --git a/src/renderer/document/styles.css b/src/renderer/document/styles.css index f3941916..2963c478 100644 --- a/src/renderer/document/styles.css +++ b/src/renderer/document/styles.css @@ -1,5 +1,5 @@ .document-tab { - @apply items-center h-full mr-px text-left level-1 gap-2 opacity-50; + @apply button flex items-center h-full mr-px text-left level-1 gap-2 opacity-50 hover:level-1; padding: 8px 8px 8px 16px; flex: 0 1 130px; diff --git a/src/renderer/document/views.cljs b/src/renderer/document/views.cljs index 5fc74c72..58004f48 100644 --- a/src/renderer/document/views.cljs +++ b/src/renderer/document/views.cljs @@ -51,7 +51,7 @@ (defn close-button [key] - [:button.icon-button.small.close-document-button + [:button.icon-button.small.close-document-button.hover:bg-transparent {:key key :title "Close document" :on-pointer-down #(.stopPropagation %) @@ -75,7 +75,7 @@ (fn [key document active?] [:> ContextMenu/Root [:> ContextMenu/Trigger - [:div.flex.button.document-tab.level-1 + [:div.document-tab {:class (when active? "active") :on-wheel #(rf/dispatch [:document/scroll (.-deltaY %)]) :on-pointer-down #(case (.-buttons %) diff --git a/src/renderer/menubar/views.cljs b/src/renderer/menubar/views.cljs index b0d380ab..bbe9f838 100644 --- a/src/renderer/menubar/views.cljs +++ b/src/renderer/menubar/views.cljs @@ -238,11 +238,6 @@ :type :checkbox :checked? [:panel/visible? :history] :action [:panel/toggle :history]} - {:key :toggle-command-history - :type :checkbox - :label "Command history" - :checked? [:panel/visible? :repl-history] - :action [:panel/toggle :repl-history]} {:key :divider-2 :type :separator} {:key :toggle-grid diff --git a/src/renderer/reepl/README.md b/src/renderer/reepl/README.md index c7f52846..9b57eb45 100644 --- a/src/renderer/reepl/README.md +++ b/src/renderer/reepl/README.md @@ -8,10 +8,10 @@ Based on reepl https://github.com/jaredly/reepl by Jared Forsyth under "ISC Lice - Conform with replumb breaking changes https://github.com/arichiardi/replumb#usage - Parinfer removed - Multiple view and style changes +- Separate styles ## Todo -- Separate styles - Use re-frame to store and retrieve state to global app-db ## Considerations diff --git a/src/renderer/reepl/codemirror.cljs b/src/renderer/reepl/codemirror.cljs index b72d947c..02f9e94d 100644 --- a/src/renderer/reepl/codemirror.cljs +++ b/src/renderer/reepl/codemirror.cljs @@ -55,8 +55,8 @@ (when-not (empty? words) {:list words :num (count words) - :active (= (get (first words) 2) text) - :show-all false + :active? (= (get (first words) 2) text) + :show-all? false :initial-text text :pos 0 :from (:start range) @@ -99,17 +99,17 @@ evt the triggering event. it will be `.preventDefault'd if there are completions to cycle through." - [{:keys [num pos active from to list initial-text] :as state} + [{:keys [num pos active? from to list initial-text] :as state} go-back? cm evt] (when (and state (or (< 1 (count list)) (and (< 0 (count list)) (not= initial-text (get (first list) 2))))) (.preventDefault evt) (let [initial-active (= initial-text (get (first list) 2)) - [active pos] (if active - (cycle-pos num pos go-back? initial-active) - [true (if go-back? (dec num) pos)]) - text (if active + [active? pos] (if active? + (cycle-pos num pos go-back? initial-active) + [true (if go-back? (dec num) pos)]) + text (if active? (get (get list pos) 2) initial-text)] ;; TODO: don't replaceRange here, instead watch the state atom and react to @@ -117,7 +117,7 @@ (.replaceRange cm text from to) (assoc state :pos pos - :active active + :active? active? :to #js {:line (.-line from) :ch (+ (count text) (.-ch from))})))) @@ -199,7 +199,7 @@ (if (cancel-keys (.-keyCode evt)) (reset! complete-atom nil) (if (cmp-show (.-keyCode evt)) - (swap! complete-atom assoc :show-all false) + (swap! complete-atom assoc :show-all? false) (when-not (cmp-ignore (.-keyCode evt)) (reset! complete-atom (repl-hint complete-word inst nil))))))) @@ -207,7 +207,7 @@ (fn [inst evt] (case (.-keyCode evt) (17 18 91 93) - (swap! complete-atom assoc :show-all true) + (swap! complete-atom assoc :show-all? true) ;; tab ;; TODO: do I ever want to use TAB normally? ;; Maybe if there are no completions... diff --git a/src/renderer/reepl/completions.cljs b/src/renderer/reepl/completions.cljs index a180b073..61474ee3 100644 --- a/src/renderer/reepl/completions.cljs +++ b/src/renderer/reepl/completions.cljs @@ -3,66 +3,38 @@ ["react" :as react] [cljs.reader] [cljs.tools.reader] - [reagent.core :as r] - [renderer.reepl.helpers :as helpers])) + [reagent.core :as ra] + [renderer.utils.dom :as dom])) -(def styles - {:completion-list {:flex-direction :row - :position :absolute - :bottom "100%" - :left 0 - :right 0 - :overflow :hidden - :font-size 12} - - :completion-show-all {:top 0 - :left 0 - :right 0 - :z-index 1000 - :flex-direction :row - :flex-wrap :wrap} - :completion-item {;; :cursor :pointer TODO: make these clickable - :padding "3px 5px" - :background-color "var(--level-0)"} - :completion-selected {:background-color "var(--level-1)"} - :completion-active {:background-color "var(--accent"}}) - -(def view (partial helpers/view styles)) - -(def canScrollIfNeeded - (some? (.-scrollIntoViewIfNeeded js/document.body))) - -(defn completion-item [_text _is-selected _is-active _set-active] +(defn completion-item + [_text _selected? _active? _set-active] (let [ref (react/createRef)] - (r/create-class + (ra/create-class {:component-did-update - (fn [this [_ _ old-is-selected]] - (let [[_ _ is-selected] (r/argv this)] - (when (and (not old-is-selected) - is-selected) - (when canScrollIfNeeded - (.scrollIntoViewIfNeeded (.-current ref) false) - (.scrollIntoView (.-current ref)))))) + (fn [this [_ _ old-selected?]] + (let [[_ _ selected?] (ra/argv this)] + (when (and (not old-selected?) + selected?) + (dom/scroll-into-view (.-current ref))))) :reagent-render - (fn [text is-selected is-active set-active] - [view {:on-click set-active - :ref ref - :style [:completion-item - (and is-selected - (if is-active - :completion-active - :completion-selected))]} + (fn [text selected? active? set-active] + [:div.p-1.level-0.text-nowrap + {:on-click set-active + :class (and selected? (if active? "bg-accent" "level-1")) + :ref ref} text])}))) -(defn completion-list [{:keys [pos list active show-all]} set-active] +(defn completion-list + [docs {:keys [pos list active? show-all?]} set-active] (let [items (map-indexed #(-> [completion-item (get %2 2) (= %1 pos) - active + active? (partial set-active %1)]) list)] - (when show-all - (into [view :completion-show-all] items)) - (into - [view :completion-list] - items))) + [:div.absolute.bottom-full.left-0.w-full.text-xs + (when docs [:div.level-1.drop-shadow.p-4.absolute.bottom-full docs]) + (into + [:div.overflow-hidden.flex + {:class (when show-all? "flex-wrap")}] + items)])) diff --git a/src/renderer/reepl/core.cljs b/src/renderer/reepl/core.cljs index 1fd5326e..6292c4db 100644 --- a/src/renderer/reepl/core.cljs +++ b/src/renderer/reepl/core.cljs @@ -1,5 +1,7 @@ (ns renderer.reepl.core (:require + ["react" :as react] + ["react-resizable-panels" :refer [Panel PanelResizeHandle]] [cljs.reader] [cljs.tools.reader] [re-frame.core :as rf] @@ -8,71 +10,14 @@ [renderer.reepl.codemirror :as code-mirror] [renderer.reepl.completions :refer [completion-list]] [renderer.reepl.handlers :as handlers] - [renderer.reepl.helpers :as helpers] [renderer.reepl.repl-items :refer [repl-items]] [renderer.reepl.subs :as subs] [replumb.core :as replumb]) (:require-macros [reagent.ratom :refer [reaction]])) -(def styles - {:container {:font-family "var(--font-mono)" - :flex 1 - :display :flex - :white-space "pre-wrap" - :flex-wrap "wrap" - :position :relative} - - :intro-message {:padding "10px 20px" - :line-height 1.5 - :border-bottom "1px solid #aaa" - :flex-direction :row - :margin-bottom 10} - :intro-code {:background-color "#eee" - :padding "0 5px"} - - :completion-container {:font-size 12} - :completion-list {:flex-direction :row - :overflow :hidden - :height 20} - :completion-empty {:color "#ccc" - ;;:font-weight :bold - :padding "3px 10px"} - - :completion-show-all {:position :absolute - :top 0 - :left 0 - :right 0 - :z-index 1000 - :flex-direction :row - :background-color "#eef" - :flex-wrap :wrap} - :completion-item {;; :cursor :pointer TODO: make these clickable - :padding "3px 5px 3px"} - :completion-selected {:background-color "#eee"} - :completion-active {:background-color "#aaa"} - - :docs {:max-height 300 - :overflow :auto - :padding "16px" - :position "absolute" - :bottom "100%" - :margin-bottom "24px" - :background "var(--level-1)" - :left 0 - :font-size "13px" - :box-shadow "0 0 15px rgba(0, 0, 0, .25)"} - - :main-caret {:padding "8px 5px 8px 10px" - :margin-right 0 - :flex-direction :row} - - :input-caret {:color "#55f" - :margin-right 10}}) - -(def view (partial helpers/view styles)) - -(defn is-valid-cljs? [source] +(defn is-valid-cljs? + [source] (try (fn [] (cljs.tools.reader/read-string source) @@ -123,7 +68,8 @@ :on-click #(rf/dispatch [:set-repl-mode mode])} mode])) -(defn repl-input [state submit repl-history? cm-opts] +(defn repl-input + [state submit panel-ref cm-opts] {:pre [(every? (comp not nil?) (map cm-opts [:on-up @@ -131,10 +77,10 @@ :complete-atom :complete-word :on-change]))]} - (let [{:keys [_pos _count _text]} @state] - [:div.flex - [:div.flex.pl-2 {:style {:font-size "12px" - :margin-top "6px"}} (replumb/get-prompt)] + (let [{:keys [_pos _count _text]} @state + repl-history? @(rf/subscribe [:panel/visible? :repl-history])] + [:div.toolbar {:style {:padding-top "0" :padding-bottom "0"}} + [:div.flex.text-xs {:style {:margin-top "7px"}} (replumb/get-prompt)] ^{:key (str (hash (:js-cm-opts cm-opts)))} [code-mirror/code-mirror (reaction (:text @state)) (merge @@ -147,20 +93,21 @@ #_[:div (repl-mode-button :cljs) (repl-mode-button :js)] - [:div.toolbar - [comp/toggle-icon-button - {:class "small" - :active? (not repl-history?) - :active-icon "chevron-up" - :active-text "show command output" - :inactive-icon "chevron-down" - :inactive-text "hide command output" - :action #(rf/dispatch [:panel/toggle :repl-history])}]]])) - -(defn docs-view [docs] - (when docs [view :docs docs])) - -(defn set-print! [log] + [comp/toggle-icon-button + {:class "hover:bg-transparent my-0" + :active? repl-history? + :active-icon "chevron-down" + :active-text "Hide command output" + :inactive-icon "chevron-up" + :inactive-text "Show command output" + :action #(when-let [panel (.-current panel-ref)] + (if (.isExpanded panel) + (.collapse panel) + (.expand panel)))} + {:style {:margin-top "0" :margin-bottom "0"}}]])) + +(defn set-print! + [log] (set! cljs.core/*print-newline* false) (set! cljs.core/*print-err-fn* (fn [& args] @@ -179,7 +126,8 @@ :history [""]}) ;; TODO: is there a macro or something that could do this cleaner? -(defn make-handlers [state] +(defn make-handlers + [state] {:add-input (partial swap! state handlers/add-input) :add-result (partial swap! state handlers/add-result) :go-up (partial swap! state handlers/go-up) @@ -188,14 +136,15 @@ :set-text (partial swap! state handlers/set-text) :add-log (partial swap! state handlers/add-log)}) -(defn repl [& {:keys [execute - _complete-word - get-docs - state - _show-value-opts - _js-cm-opts - _on-cm-init]}] - (let [repl-history? (rf/subscribe [:panel/visible? :repl-history]) +(defn repl + [& {:keys [execute + _complete-word + get-docs + state + _show-value-opts + _js-cm-opts + _on-cm-init]}] + (let [panel-ref (react/createRef) state (or state (r/atom initial-state)) {:keys [add-input @@ -233,20 +182,33 @@ show-value-opts js-cm-opts on-cm-init]}] - [:div - (when @repl-history? [repl-items @items (assoc show-value-opts :set-text set-text)]) - [view :container - (when @docs [docs-view @docs]) + [:<> + [:> PanelResizeHandle + {:id "repl-resize-handle" + :className "resize-handle"}] + [:> Panel + {:id "repl-panel" + :minSize 10 + :defaultSize 0 + :collapsible true + :order 3 + :ref panel-ref + :onCollapse #(rf/dispatch-sync [:panel/collapse :repl-history]) + :onExpand #(rf/dispatch-sync [:panel/expand :repl-history])} + [repl-items @items (assoc show-value-opts :set-text set-text)]] + + [:div.relative.whitespace-pre-wrap.font-mono [completion-list + @docs @complete-atom ;; TODO: this should also replace the text.... identity - #_(swap! complete-atom assoc :pos % :active true)] + #_(swap! complete-atom assoc :pos % :active? true)] (let [_items @items] ; TODO: This needs to be removed [repl-input (subs/current-text state) submit - @repl-history? + panel-ref {:complete-word complete-word :on-up go-up :on-down go-down diff --git a/src/renderer/reepl/handlers.cljs b/src/renderer/reepl/handlers.cljs index f4a311dc..67a490a3 100644 --- a/src/renderer/reepl/handlers.cljs +++ b/src/renderer/reepl/handlers.cljs @@ -1,32 +1,40 @@ (ns renderer.reepl.handlers) -(defn clear-items [db] +(defn clear-items + [db] (assoc db :items [])) -#_(defn init [db data] +#_(defn init + [db data] (merge db data)) -#_(defn add-item [db item] +#_(defn add-item + [db item] (update db :items conj item)) -#_(defn add-items [db items] +#_(defn add-items + [db items] (update db :items concat items)) -(defn add-input [db input] +(defn add-input + [db input] (let [inum (count (:history db))] (-> db (assoc :hist-pos 0) (update :history conj "") (update :items conj {:type :input :text input :num inum})))) -(defn add-result [db error? value] +(defn add-result + [db error? value] (update db :items conj {:type (if error? :error :output) :value value})) -(defn add-log [db val] +(defn add-log + [db val] (update db :items conj {:type :log :value val})) -(defn set-text [db text] +(defn set-text + [db text] (let [history (:history db) pos (:hist-pos db) idx (- (count history) pos 1)] @@ -38,7 +46,8 @@ (assoc history (dec (count history)) text) (conj history text)))))) -(defn go-up [db] +(defn go-up + [db] (let [pos (:hist-pos db) len (count (:history db)) new-pos (if (>= pos (dec len)) @@ -46,7 +55,8 @@ (inc pos))] (assoc db :hist-pos new-pos))) -(defn go-down [db] +(defn go-down + [db] (let [pos (:hist-pos db) new-pos (if (<= pos 0) 0 diff --git a/src/renderer/reepl/helpers.cljs b/src/renderer/reepl/helpers.cljs deleted file mode 100644 index 5f7131fe..00000000 --- a/src/renderer/reepl/helpers.cljs +++ /dev/null @@ -1,58 +0,0 @@ -(ns renderer.reepl.helpers - #_(:require [reagent.core :as r])) - -(def text-style {:display "inline-block" - :flex-shrink 0 - :box-sizing "border-box"}) - -(def view-style {:display "flex" - :flex-direction "column" - :flex-shrink 0 - :box-sizing "border-box"}) - -(defn get-styles [styles style-prop] - (cond - (not style-prop) {} - (keyword? style-prop) (styles style-prop) - (sequential? style-prop) (reduce (fn [a b] (merge a (get-styles styles b))) {} style-prop) - :else style-prop)) - -(defn parse-props [styles default-style props] - (if (keyword? props) - (parse-props styles default-style {:style props}) - (merge {:style (merge default-style (get-styles styles (:style props)))} - (dissoc props :style))) - #_(if (keyword? props) - {:style (merge default-style (props styles))} - (let [style-prop (:style props) - style (if (keyword? style-prop) - (styles style-prop) - style) - style (merge default-style style) - props (merge {:style style} (dissoc props :style))] - props))) - -(defn better-el [dom-el default-style styles props & children] - (let [[props children] - (if (or (keyword? props) (map? props)) - [props children] - [nil (concat [props] children)])] - - (vec (concat [dom-el (parse-props styles default-style props)] children)))) - -(def view (partial better-el :div view-style)) -(def text (partial better-el :span text-style)) -; TODO: have the button also stop-propagation -#_(defn hoverable [_config & _children] - (let [hovered (r/atom false)] - (fn [{:keys [style hover-style el props]} - & children] - (into - [el (assoc props - :style (if @hovered - (merge style hover-style) - style) - :on-pointer-over (fn [] (reset! hovered true) nil) - :on-pointer-out (fn [] (reset! hovered false) nil))] - - children)))) diff --git a/src/renderer/reepl/repl_items.cljs b/src/renderer/reepl/repl_items.cljs index bb4f13a5..080b6eeb 100644 --- a/src/renderer/reepl/repl_items.cljs +++ b/src/renderer/reepl/repl_items.cljs @@ -5,75 +5,37 @@ [cljs.tools.reader] [reagent.core :as r] [renderer.reepl.codemirror :as codemirror] - [renderer.reepl.helpers :as helpers] [renderer.reepl.show-value :refer [show-value]])) -(def styles - {:repl-items {:flex 1 - :overflow-y "auto" - :overflow-x "hidden" - :height "200px" - :padding "3px" - :flex-basis "100%" - :border-bottom "1px solid var(--border-color)" - :flex-shrink 1} - :repl-item {:flex-direction :row - :font-size 12 - :font-family "Source Code Pro, monospace" - :padding "3px 5px"} - - :intro-message {:padding "10px 27px" - :line-height 1.5 - :border-bottom "1px solid var(--border-color)" - :flex-direction :row} - - :input-item {} - :output-item {} - :error-item {:color "var(--error-color)"} - :underlying-error {:margin-left 10} - :caret {:color "var(--font-color-disabled)" - :font-weight "bold" - :font-size 12 - :flex-direction :row} - :input-text {:flex 1 - :cursor :pointer - :word-wrap :break-word} - :output-caret {} - :output-value {:flex 1 - :word-wrap :break-word}}) - -(def view (partial helpers/view styles)) -(def text (partial helpers/text styles)) - (defmulti repl-item (fn [item _opts] (:type item))) (defmethod repl-item :input [{:keys [_num text]} opts] - [view {:style [:repl-item :input-item]} - [view {:style [:caret]} "=>"] - [view {:style :input-text - :on-click (partial (:set-text opts) text)} + [:div.repl-item + [:div.text-disabled.font-bold "=>"] + [:div.flex-1.cursor-pointer.break-words + {:on-click (partial (:set-text opts) text)} [codemirror/colored-text text]]]) (defmethod repl-item :log [{:keys [value]} opts] - [view {:style [:repl-item :log-item]} + [:div.repl-item [show-value value nil opts]]) (defmethod repl-item :error [{:keys [value]} _opts] (let [message (.-message value) underlying (.-cause value)] - [view {:style [:repl-item :output-item :error-item]} + [:div.repl-item.text-error message (when underlying ;; TODO: also show stack? - [text :underlying-error (.-message underlying)])])) + [:span.ml-2.5 (.-message underlying)])])) (defmethod repl-item :output [{:keys [value]} opts] - [view {:style [:repl-item :output-item]} - [view :output-value [show-value value nil opts]]]) + [:div..repl-item + [:div.flex-1.break-words [show-value value nil opts]]]) (defn repl-items [_] (let [ref (react/createRef)] @@ -85,6 +47,6 @@ :reagent-render (fn [items opts] (into - [view {:style :repl-items - :ref ref}] + [:div.flex-1.border-b.border-default.h-full.overflow-auto + {:ref ref}] (map #(repl-item % opts) items)))}))) diff --git a/src/renderer/reepl/replumb.cljs b/src/renderer/reepl/replumb.cljs index e3accbf7..777b9e33 100644 --- a/src/renderer/reepl/replumb.cljs +++ b/src/renderer/reepl/replumb.cljs @@ -80,7 +80,8 @@ cljs.js/*load-fn* (cb false (:error result)) (cb true (aget js/window "last_repl_value")))))) -(defn get-first-form [text] +(defn get-first-form + [text] ;; parse #js {} correctly (binding [cljs.tools.reader/*data-readers* tags/*cljs-data-readers*] (let [rr (string-push-back-reader text) @@ -89,7 +90,8 @@ cljs.js/*load-fn* s-pos (.-s-pos (.-rdr ^js rr))] [form s-pos]))) -(defn run-repl-multi [text opts cb] +(defn run-repl-multi + [text opts cb] (let [text (.trim text) [_form pos] (get-first-form text) source (.slice text 0 pos) @@ -112,14 +114,16 @@ cljs.js/*load-fn* ;; Trying to get expressions + statements to play well together ;; TODO: is this a better way? The `do' stuff seems to work alright ... although ;; it won't work if there are other `ns' statements inside there... -#_(defn run-repl-experimental* [text opts cb] +#_(defn run-repl-experimental* \ + [text opts cb] (let [fixed (make-last-expr-set-val text "last_repl_value")] (if fixed (jsc-run fixed cb) (replumb/read-eval-call opts #(cb (replumb/success? %) (replumb/unwrap-result %)) text)))) -#_(defn fix-ns-do [text] +#_(defn fix-ns-do + [text] ;; parse #js {} correctly (binding [cljs.tools.reader/*data-readers* tags/*cljs-data-readers*] (let [rr (string-push-back-reader text) @@ -137,7 +141,8 @@ cljs.js/*load-fn* (.slice text s-pos) ")"))))) -#_(defn run-repl* [text opts cb] +#_(defn run-repl* + [text opts cb] (replumb/read-eval-call opts #(cb @@ -283,12 +288,14 @@ cljs.js/*load-fn* #(-> [% (str %) (str %)]) (filter matches? names)))))) -(defn process-apropos [text] +(defn process-apropos + [text] (if (zero? (.indexOf text "js/")) (js-completion (.slice text 3)) (cljs-completion text))) -(defn get-forms [m] +(defn get-forms + [m] (cond (:forms m) (:forms m) (:arglists m) (let [arglists (:arglists m)] @@ -300,7 +307,8 @@ cljs.js/*load-fn* arglists))))) ;; Copied & modified from cljs.repl/print-doc -(defn get-doc [m] +(defn get-doc + [m] (merge {:name (str (when-let [ns (:ns m)] (str ns "/")) (:name m)) :type (cond (:protocol m) :protocol @@ -318,7 +326,8 @@ cljs.js/*load-fn* (when (:protocol m) {:protocol-methods (:methods m)})))) -(defn doc-from-sym [sym] +(defn doc-from-sym + [sym] (cond (docs/special-doc-map sym) (get-doc (docs/special-doc sym)) (docs/repl-special-doc-map sym) (get-doc (docs/repl-special-doc sym)) @@ -339,7 +348,8 @@ cljs.js/*load-fn* :repl-special-function "REPL Special Function"}) ;; Copied & modified from cljs.repl/print-doc -(defn print-doc [doc] +(defn print-doc + [doc] (println (:name doc)) (println) (when-not (= :normal (:type doc)) diff --git a/src/renderer/reepl/show_devtools.cljs b/src/renderer/reepl/show_devtools.cljs index 016fcab7..a0894d4e 100644 --- a/src/renderer/reepl/show_devtools.cljs +++ b/src/renderer/reepl/show_devtools.cljs @@ -1,23 +1,22 @@ (ns renderer.reepl.show-devtools (:require - #_[cljs.pprint :as pprint] [devtools.formatters.core :as devtools] [reagent.core :as r] [clojure.string :as str])) -#_(defn pprint-str [val] - (pprint/write val :stream nil)) - -(defn js-array? [val] +(defn js-array? + [val] (= js/Array (type val))) -(defn parse-style [raw] +(defn parse-style + [raw] (into {} (map (fn [line] (let [[k v] (str/split line ":")] [(keyword k) v])) (str/split raw ";")))) -(defn show-el [val show-value] +(defn show-el + [val show-value] (let [type (first val) opts (second val) children (drop 2 val)] @@ -27,23 +26,23 @@ [(keyword type) {:style (when opts (parse-style (.-style opts)))}] (map #(if-not (js-array? %) % (show-el % show-value)) children))))) - -(defn openable [header val config show-value] +(defn openable + [header val config show-value] (let [open (r/atom false)] (fn [_ _] (let [is-open @open] [:div.flex.flex-col [:div.flex - [:div.flex.value-toggle + [:div.flex.cursor-pointer.px-1 {:on-click #(swap! open not)} (if is-open "▼" "▶")] (show-el header show-value)] (when is-open (show-el (devtools/body-api-call val config) show-value))])))) - ;; see https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U/preview -(defn show-devtools [val config show-value] +(defn show-devtools + [val config show-value] (when-not (var? val) (let [header (try (devtools/header-api-call val config) diff --git a/src/renderer/reepl/show_function.cljs b/src/renderer/reepl/show_function.cljs index ddfe9ba3..7f413284 100644 --- a/src/renderer/reepl/show_function.cljs +++ b/src/renderer/reepl/show_function.cljs @@ -1,30 +1,31 @@ (ns renderer.reepl.show-function (:require - [clojure.string :as str] - [renderer.reepl.helpers :as helpers])) - -(def text (partial helpers/text)) + [clojure.string :as str])) (def cljs-fn-prefix "cljs$core$IFn$_invoke$arity$") -(defn recover-cljs-name [parts] +(defn recover-cljs-name + [parts] (-> (str/join \. (butlast parts)) (str \/ (last parts)) demunge)) -(defn get-cljs-arities [fn] +(defn get-cljs-arities + [fn] (map #(aget fn %) (filter #(.startsWith % cljs-fn-prefix) (js->clj (js/Object.keys fn))))) -(defn get-fn-summary [fn] +(defn get-fn-summary + [fn] (let [source (str fn) args (second (re-find #"\(([^\)]+)\)" source))] (map demunge (str/split args \,)))) -(defn get-function-forms [fn] +(defn get-function-forms + [fn] (let [arities (get-cljs-arities fn) arities (if (empty? arities) [fn] @@ -32,30 +33,27 @@ (map get-fn-summary arities))) -(defn get-fn-name [fn] +(defn get-fn-name + [fn] (let [parts (.split (.-name fn) \$)] (cond (empty? (.-name fn)) "*anonymous*" (= 1 (count parts)) (.-name fn) :else (recover-cljs-name parts)))) -(defn str-fn-forms [forms] - (str - \[ (str/join "] [" (map (partial str/join " ") forms)) \])) +(defn str-fn-forms + [forms] + (str \[ (str/join "] [" (map (partial str/join " ") forms)) \])) -(defn show-fn-with-docs [get-doc fn _ _] +(defn show-fn-with-docs + [get-doc fn _ _] (when (= js/Function (type fn)) (let [docs (get-doc (symbol (get-fn-name fn))) is-native-fn (.match (str fn) #"\{ \[native code\] \}$")] (if docs [:div - [text :function-docs - docs]] + [:span.function.function-docs docs]] [:div - [text :function-head "fn " (get-fn-name fn)] - [text :function-arities (str-fn-forms (get-function-forms fn))] - [text :function-body - (when is-native-fn "[native code]")]])))) - -#_(defn show-fn [f config show-value] - (show-fn-with-docs (fn [_] nil) f config show-value)) + [:span.function.function-head "fn " (get-fn-name fn)] + [:span.function.function-arities (str-fn-forms (get-function-forms fn))] + [:span.function.function-body (when is-native-fn "[native code]")]])))) diff --git a/src/renderer/reepl/show_value.cljs b/src/renderer/reepl/show_value.cljs index 6b0307a9..23958946 100644 --- a/src/renderer/reepl/show_value.cljs +++ b/src/renderer/reepl/show_value.cljs @@ -2,15 +2,18 @@ (:require [cljs.pprint :as pprint])) -(defn pprint-str [val] +(defn pprint-str + [val] (pprint/write val :stream nil)) -(defn show-str [val] +(defn show-str + [val] (if (string? val) val (pprint-str val))) -(defn show-value- [val config showers] +(defn show-value- + [val config showers] (loop [shower-list showers] (if (empty? shower-list) (throw (js/Error. (str "No shower for value " val))) diff --git a/src/renderer/reepl/styles.css b/src/renderer/reepl/styles.css index 640b5808..808ac18a 100644 --- a/src/renderer/reepl/styles.css +++ b/src/renderer/reepl/styles.css @@ -1,5 +1,7 @@ -.value-toggle { - font-size: 9; - padding: 4; - cursor: pointer; +.repl-item { + @apply font-mono p-1 flex text-xs; +} + +.function { + @apply inline-block shrink-0 box-border ; } diff --git a/src/renderer/reepl/subs.cljs b/src/renderer/reepl/subs.cljs index dde8e6bd..8fefd52a 100644 --- a/src/renderer/reepl/subs.cljs +++ b/src/renderer/reepl/subs.cljs @@ -2,10 +2,12 @@ (:require-macros [reagent.ratom :refer [reaction]])) -(defn items [db] +(defn items + [db] (reaction (:items @db))) -(defn current-text [db] +(defn current-text + [db] (let [idx (reaction (:hist-pos @db)) history (reaction (:history @db))] (reaction (let [items @history diff --git a/src/renderer/reepl/views.cljs b/src/renderer/reepl/views.cljs index 7ae8e3c7..3e543dc2 100644 --- a/src/renderer/reepl/views.cljs +++ b/src/renderer/reepl/views.cljs @@ -11,22 +11,23 @@ (defonce repl-state (r/atom reepl/initial-state)) -(defn maybe-fn-docs [fn] +(defn maybe-fn-docs + [fn] (let [doc (replumb/doc-from-sym fn)] (when (:forms doc) (with-out-str (replumb/print-doc doc))))) -(defn main-view [] - [:div.flex.flex-col.overflow-visible - [reepl/repl - :execute #(replumb/run-repl (if (= @(rf/subscribe [:repl-mode]) :cljs) %1 (str "(js/eval \"" %1 "\")")) {:warning-as-error true} %2) - :complete-word replumb/process-apropos - :get-docs replumb/process-doc - :state repl-state - :show-value-opts - {:showers [show-devtools/show-devtools - (partial show-function/show-fn-with-docs maybe-fn-docs)]} - :js-cm-opts {:mode (if (= @(rf/subscribe [:repl-mode]) :cljs) "clojure" "javascript") - :keyMap "default" - :showCursorWhenSelecting true}]]) +(defn root + [] + [reepl/repl + :execute #(replumb/run-repl (if (= @(rf/subscribe [:repl-mode]) :cljs) %1 (str "(js/eval \"" %1 "\")")) {:warning-as-error true} %2) + :complete-word replumb/process-apropos + :get-docs replumb/process-doc + :state repl-state + :show-value-opts + {:showers [show-devtools/show-devtools + (partial show-function/show-fn-with-docs maybe-fn-docs)]} + :js-cm-opts {:mode (if (= @(rf/subscribe [:repl-mode]) :cljs) "clojure" "javascript") + :keyMap "default" + :showCursorWhenSelecting true}]) diff --git a/src/renderer/styles.css b/src/renderer/styles.css index 02431cdd..03f37f5f 100644 --- a/src/renderer/styles.css +++ b/src/renderer/styles.css @@ -7,6 +7,10 @@ background: var(--level-1); } + .bg-accent { + background: var(--accent); + } + .drag { -webkit-app-region: drag; } @@ -100,15 +104,12 @@ &:hover { cursor: pointer; transition: all 75ms; - color: var(--font-color-hovered); - } - - &:active { background-color: var(--overlay); + color: var(--font-color-hovered); } &.selected { - background-color: var(--accent) !important; + @apply bg-accent !important; color: var(--accent-inverted); } diff --git a/src/renderer/utils/dom.cljs b/src/renderer/utils/dom.cljs index 1d8e8e45..356d793a 100644 --- a/src/renderer/utils/dom.cljs +++ b/src/renderer/utils/dom.cljs @@ -25,5 +25,4 @@ (defn scroll-into-view [el] - (.scrollIntoView el #js {:behavior "smooth" - :block "nearest"})) + (.scrollIntoView el #js {:block "nearest"})) diff --git a/src/renderer/views.cljs b/src/renderer/views.cljs index e45a4496..c4d87824 100644 --- a/src/renderer/views.cljs +++ b/src/renderer/views.cljs @@ -23,12 +23,6 @@ [renderer.tree.views :as tree] [renderer.window.views :as win])) -(defn command-input [] - [:div.flex.flex-col.level-0.relative.overflow-visible - {:style {:font-size "10px" - :user-select "text"}} - [repl/main-view]]) - (defn frame-panel [] (let [rulers? @(rf/subscribe [:rulers?]) @@ -115,19 +109,12 @@ :id "editor-group" :autoSaveId "editor-group"} [:> Panel {:id "editor-panel" - :minSize 10 + :minSize 20 + :defaultSize 100 :order 1} [center-top-group]] - [timeline/root]]) - -(defn center-panel - [] - [:> Panel - {:id "center-panel" - :order 1} - [:div.flex.h-full.flex-col - [editor] - [command-input]]]) + [timeline/root] + [repl/root]]) (defn tree-panel [] @@ -171,7 +158,11 @@ {:direction "horizontal" :id "center-group" :autoSaveId "center-group"} - [center-panel] + [:> Panel + {:id "center-panel" + :order 1} + [:div.flex.h-full.flex-col + [editor]]] [:> PanelResizeHandle {:id "properties-resize-handle" :className "resize-handle"}]