diff --git a/CHANGELOG.md b/CHANGELOG.md index 143eef1e..7ab2796e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. This change ## Unreleased +#### Added + +- Added rewrite-clj 1.1.47 dependency + +#### Changed + +- Removed highlight.js dependency - Upgrade clojure & clojurescript to 1.11 - Upgrade zprint to 1.2.7 - Upgrade malli to 0.11.0 diff --git a/examples/todomvc/shadow-cljs.edn b/examples/todomvc/shadow-cljs.edn index dae03601..39bb8801 100644 --- a/examples/todomvc/shadow-cljs.edn +++ b/examples/todomvc/shadow-cljs.edn @@ -5,7 +5,7 @@ [day8.re-frame/tracing "0.6.2"] [com.yahoo.platform.yui/yuicompressor "2.4.8" :exclusions [rhino/js]] - [superstructor/re-highlight "2.0.2"] + [rewrite-clj/rewrite-clj "1.1.47"] [secretary "1.2.3"] [binaryage/devtools "1.0.6"] [metosin/malli "0.11.0"]] diff --git a/project.clj b/project.clj index 8bf9c065..234cec3d 100644 --- a/project.clj +++ b/project.clj @@ -11,12 +11,8 @@ [com.yahoo.platform.yui/yuicompressor "2.4.8" :exclusions [rhino/js]] [zprint "1.2.7"] - [superstructor/re-highlight "2.0.2"] - ;; re-highlight only has a transitive dependency on highlight.js for - ;; shadow-cljs builds, so we need to declare a dependency on cljsjs/highlight - ;; for 10x to support other build systems. - [cljsjs/highlight "10.3.1-0"] - [org.clojure/tools.logging "1.2.4"]] + [org.clojure/tools.logging "1.2.4"] + [rewrite-clj/rewrite-clj "1.1.47"]] :plugins [[day8/lein-git-inject "0.0.15"] [lein-less "RELEASE"]] diff --git a/src/day8/re_frame_10x/panels/event/subs.cljs b/src/day8/re_frame_10x/panels/event/subs.cljs index c4f7c2cc..ba7603de 100644 --- a/src/day8/re_frame_10x/panels/event/subs.cljs +++ b/src/day8/re_frame_10x/panels/event/subs.cljs @@ -152,12 +152,28 @@ (rf/reg-sub ::highlighted-form-bounds :<- [::highlighted-form] - :<- [::form-for-epoch] + :<- [::zprint-form-for-epoch] (fn [[highlighted-form form] _] - (find-bounds (str form) + (find-bounds form (:form highlighted-form) (:num-seen highlighted-form)))) +(rf/reg-sub + ::highlighted? + :<- [::zprint-form-for-epoch] + :<- [::highlighted-form-bounds] + (fn [[zp [left-bound _]] [_ [line char]]] + (when (pos? left-bound) + (let [line (dec line) + char (dec char) + line-counts (map (comp inc count) + (clojure.string/split-lines zp))] + (->> line-counts + (take line) + (apply +) + (+ char) + (= left-bound)))))) + (rf/reg-sub ::show-all-code? :<- [::root] diff --git a/src/day8/re_frame_10x/panels/event/views.cljs b/src/day8/re_frame_10x/panels/event/views.cljs index 3a0b114f..2525bb55 100644 --- a/src/day8/re_frame_10x/panels/event/views.cljs +++ b/src/day8/re_frame_10x/panels/event/views.cljs @@ -2,7 +2,6 @@ (:require-macros [day8.re-frame-10x.components.re-com :refer [handler-fn]]) (:require - [re-highlight.core :as re-highlight] [day8.re-frame-10x.inlined-deps.garden.v1v3v10.garden.units :refer [px ms]] [day8.re-frame-10x.inlined-deps.spade.git-sha-93ef290.core :refer [defclass]] [day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.core :as r] @@ -21,7 +20,7 @@ [day8.re-frame-10x.panels.settings.subs :as settings.subs] [day8.re-frame-10x.fx.clipboard :as clipboard] [day8.re-frame-10x.tools.pretty-print-condensed :as pp] - [day8.re-frame-10x.tools.datafy :as tools.datafy])) + [day8.re-frame-10x.tools.highlight-hiccup :refer [str->hiccup]])) ;; Terminology: ;; Form: a single Clojure form (may have nested children) @@ -79,34 +78,11 @@ before (subs form-str 0 start-idx) highlight (subs form-str start-idx end-idx) after (subs form-str end-idx)] - ; DC: We get lots of React errors if we don't force a creation of a new element when the highlight changes. Not really sure why... - ;; Possibly relevant? https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node ^{:key (gensym)} - ;; At some point around March 2021 Highlight.js changed their API significantly (e.g. highlightBlock -> highlightElement). - ;; re-highlight, the wrapper around Highlight.js, depends on a modern version of Highlight.js with highlightElement. - ;; Prior to the below check being added, a transitive dependency or direct project dependency that overrides - ;; the version of Highlight.js with an older release than that reqeusted by re-highlight would cause 10x to crash. - ;; Therefore, we added this check to ensure that prior to attempting to render source code with Highlight.js we check - ;; that a compatible dependency has been loaded. - (if (re-highlight/hljs-compatible?) - [rc/box + [rc/box :class (code-style ambiance syntax-color-scheme show-all-code?) :attr {:on-double-click (handler-fn (rf/dispatch [::event.events/set-show-all-code? (not show-all-code?)]))} - :child (if (some? highlighted-form) - [#_re-highlight/highlight :span {:language "clojure"} - (list ^{:key "before"} before - ^{:key "hl"} [:span.code-listing--highlighted highlight] - ^{:key "after"} after)] - [#_re-highlight/highlight :span {:language "clojure"} - form-str])] - [rc/v-box - :class (hljs-error-style ambiance syntax-color-scheme) - :children [[rc/p - "re-frame-10x found a version mismatch between the Highlight.js loaded and the one that it expects to use."] - [rc/p - "As a result, it can't display the source code for this function."] - [rc/p - "To fix this, please examine this application's dependency tree to see how an old version of Highlight.js is being pulled in (probably transitively) and perhaps then use an appropriate exclusion for that dependency."]]])))}))) + :child [str->hiccup form-str]]))}))) (defclass clipboard-notification-style [_] diff --git a/src/day8/re_frame_10x/styles.cljs b/src/day8/re_frame_10x/styles.cljs index dbb6a4a4..bcbe48df 100644 --- a/src/day8/re_frame_10x/styles.cljs +++ b/src/day8/re_frame_10x/styles.cljs @@ -553,3 +553,21 @@ [:5% {:margin-left "0px" :opacity "1"}] [:90% {:opacity "1"}])])) + +(defclass clj-symbol [] {:color nord10}) + +(defclass clj-core-macro [] {:color nord11}) + +(defclass clj-keyword [] {:color nord15}) + +(defclass clj-number [] {:color nord12}) + +(defclass clj-string [] {:color nord14}) + +(defclass clj-nil [] {:color nord3}) + +(defclass clj-boolean [] {:color nord3}) + +(defclass clj-highlighted [] {:background-color nord13}) + +(defclass clj-seq [] {}) \ No newline at end of file diff --git a/src/day8/re_frame_10x/tools/highlight_hiccup.cljs b/src/day8/re_frame_10x/tools/highlight_hiccup.cljs new file mode 100644 index 00000000..89b380cd --- /dev/null +++ b/src/day8/re_frame_10x/tools/highlight_hiccup.cljs @@ -0,0 +1,103 @@ +;; TODO: make this a standalone library + +(ns day8.re-frame-10x.tools.highlight-hiccup + (:require [clojure.walk :as walk] + [rewrite-clj.zip :as rz] + [rewrite-clj.node.token :refer [SymbolNode TokenNode]] + [rewrite-clj.node.whitespace :refer [WhitespaceNode NewlineNode CommaNode]] + [rewrite-clj.node.keyword :refer [KeywordNode]] + [rewrite-clj.node.stringz :refer [StringNode]] + [rewrite-clj.node.seq :refer [SeqNode]] + [day8.re-frame-10x.styles :as styles] + [day8.re-frame-10x.inlined-deps.re-frame.v1v1v2.re-frame.core :as rf] + [day8.re-frame-10x.panels.event.subs :as event.subs])) + + + +(def clj-core-macros #{'and 'binding 'case 'catch 'comment 'cond 'cond-> 'cond->> 'condp 'def + 'defmacro 'defn 'defn- 'defmulti 'defmethod 'defonce 'defprotocol 'deftype + 'do 'dotimes 'doseq 'dosync 'fn 'for 'future 'if 'if-let 'if-not 'import 'let + 'letfn 'locking 'loop 'ns 'or 'proxy 'quote 'recur 'set! 'struct-map 'sync 'throw + 'try 'when 'when-first 'when-let 'when-not 'when-some 'while}) + +(defn selected-style [{:keys [position]}] + (when @(rf/subscribe [::event.subs/highlighted? position]) + (styles/clj-highlighted))) + +(defmulti form type) + +(defmethod form :default [node] [:span.clj-unknown {:data-node (type node)} (str (type node))]) + +(defmulti tf2 (comp type :value)) + +(defmethod tf2 (type true) [{:keys [string-value]}] + [:code.clj__boolean {:class (styles/clj-boolean)} + string-value]) + +(defmethod tf2 (type 0) [{:keys [string-value]}] + [:code.clj_number {:class (styles/clj-number)} + string-value]) + +(defmethod tf2 (type nil) [{:keys [string-value]}] + [:code.clj__nil {:class (styles/clj-nil)} string-value]) + +(defmethod tf2 :default [{:keys [string-value] :as node}] + [:span.clj__token (str (keys node)) string-value]) + +(defmethod form TokenNode [node] + [tf2 node]) + +(defmethod form (type []) [node] node) + +(defmethod form CommaNode [node] [:span.clj__comma ","]) + +(defmulti seq-form :tag) + +(defmethod seq-form :default [{:keys [tag]}] + [:code.clj__unknown tag]) + +(defmethod seq-form :list [node] + (into [:code.seq {:class [(styles/clj-seq) + (selected-style node)]}] + (concat [ "("] (:children node) [")"]))) + +(defmethod seq-form :vector [node] + (into [:code.clj__seq] (concat ["["] (:children node) ["]"]))) + +(defmethod seq-form :map [node] + (into [:code.clj__map {:class [(selected-style node)]}] + (concat ["{"] (:children node) ["}"]))) + +(defmethod form SeqNode [node] + (seq-form node)) + +(defmethod form SymbolNode [{:keys [value string-value] :as node}] + [:code.clj__symbol {:class [(if (clj-core-macros value) + (styles/clj-core-macro) + (styles/clj-symbol)) + (selected-style node)]} + string-value]) + +(defmethod form WhitespaceNode [node] + [:code.clj__whitespace {:style {:white-space "pre"}} + (:whitespace node)]) + +(defmethod form NewlineNode [_] [:br]) + +(defmethod form KeywordNode [{:keys [k] :as node}] + [:code.clj__keyword {:class [(styles/clj-keyword) + (selected-style node)]} + (str k)]) + +(defmethod form StringNode [{:keys [lines]}] + [:code.clj__string {:class (styles/clj-string)} + \" (apply str lines) \"]) + +(defn str->hiccup [s] + (let [positional-ast + (-> s + (rz/of-string {:track-position? true}) + (rz/postwalk #(rz/edit* % assoc + :position (rz/position %))) + rz/node)] + (walk/postwalk #(cond-> % (record? %) form) positional-ast)))