|
4 | 4 | ["@codemirror/state" :refer [EditorState]] |
5 | 5 | ["@codemirror/view" :as view :refer [EditorView]] |
6 | 6 | [nextjournal.clerk.sci-viewer :as sv] |
7 | | - [nextjournal.clerk.viewer :as clerk.viewer] |
| 7 | + [nextjournal.clerk.viewer :as v] |
8 | 8 | [applied-science.js-interop :as j] |
| 9 | + [shadow.resource :as rc] |
9 | 10 | [clojure.string :as str] |
10 | 11 | [nextjournal.clojure-mode :as cm-clj] |
11 | | - [nextjournal.clojure-mode.demo.sci :as sci] |
| 12 | + [nextjournal.livedoc :as livedoc] |
| 13 | + [nextjournal.clojure-mode.demo.sci :as demo.sci] |
12 | 14 | [nextjournal.clojure-mode.keymap :as keymap] |
13 | 15 | [nextjournal.clojure-mode.live-grammar :as live-grammar] |
14 | 16 | [nextjournal.clojure-mode.test-utils :as test-utils] |
15 | | - [react] |
| 17 | + ["react" :as react] |
16 | 18 | [reagent.core :as r] |
17 | 19 | [reagent.dom :as rdom])) |
18 | 20 |
|
19 | 21 | (def theme |
20 | 22 | (.theme EditorView |
21 | 23 | (j/lit {".cm-content" {:white-space "pre-wrap" |
22 | | - :padding "10px 0"} |
23 | | - "&.cm-focused" {:outline "none"} |
| 24 | + :padding "10px 0" |
| 25 | + :flex "1 1 0"} |
| 26 | + |
| 27 | + "&.cm-focused" {:outline "0 !important"} |
24 | 28 | ".cm-line" {:padding "0 9px" |
25 | 29 | :line-height "1.6" |
26 | 30 | :font-size "16px" |
|
51 | 55 |
|
52 | 56 | (defn editor [source {:keys [eval?]}] |
53 | 57 | (r/with-let [!view (r/atom nil) |
54 | | - last-result (when eval? (r/atom (sci/eval-string source))) |
| 58 | + last-result (when eval? (r/atom (demo.sci/eval-string source))) |
55 | 59 | mount! (fn [el] |
56 | 60 | (when el |
57 | 61 | (reset! !view (new EditorView |
58 | 62 | (j/obj :state |
59 | 63 | (test-utils/make-state |
60 | 64 | (cond-> #js [extensions] |
61 | | - eval? (.concat #js [(sci/extension {:modifier "Alt" |
62 | | - :on-result (partial reset! last-result)})])) |
| 65 | + eval? (.concat #js [(demo.sci/extension {:modifier "Alt" |
| 66 | + :on-result (partial reset! last-result)})])) |
63 | 67 | source) |
64 | 68 | :parent el)))))] |
65 | 69 | [:div |
|
74 | 78 | (react/isValidElement result) result |
75 | 79 | 'else (sv/inspect-paginated result)))])] |
76 | 80 | (finally |
77 | | - (j/call @!view :destroy)))) |
78 | | - |
| 81 | + (j/call @!view :destroy)))) |
| 82 | + |
| 83 | +;; Markdown editors |
| 84 | +(defn markdown-editor [{:keys [doc extensions]}] |
| 85 | + [:div {:ref (fn [^js el] |
| 86 | + (when el |
| 87 | + (some-> el .-editorView .destroy) |
| 88 | + (j/assoc! el :editorView |
| 89 | + (EditorView. (j/obj :parent el |
| 90 | + :state (.create EditorState |
| 91 | + (j/obj :doc (str/trim doc) |
| 92 | + :extensions (into-array |
| 93 | + (cond-> [(syntaxHighlighting defaultHighlightStyle) |
| 94 | + (foldGutter) |
| 95 | + (.of view/keymap cm-clj/complete-keymap) |
| 96 | + (history) |
| 97 | + (.of view/keymap historyKeymap) |
| 98 | + theme |
| 99 | + livedoc/markdown-language-support] |
| 100 | + (seq extensions) |
| 101 | + (concat extensions))))))))))}]) |
79 | 102 |
|
80 | 103 | (defn samples [] |
81 | 104 | (into [:<>] |
|
123 | 146 | (when-not (zero? i) [:span " + "]) |
124 | 147 | [:kbd.kbd k]]) keys)))) |
125 | 148 |
|
126 | | -(defn key-bindings-table [] |
| 149 | +(defn key-bindings-table [keymap] |
127 | 150 | [:table.w-full.text-sm |
128 | 151 | [:thead |
129 | 152 | [:tr.border-t |
|
132 | 155 | [:th.px-3.py-1.align-top.text-left.text-xs.uppercase.font-normal.black-50 "Alternate Binding"] |
133 | 156 | [:th.px-3.py-1.align-top.text-left.text-xs.uppercase.font-normal.black-50 {:style {:min-width 290}} "Description"]]] |
134 | 157 | (into [:tbody] |
135 | | - (->> keymap/paredit-keymap* |
136 | | - (merge (sci/keymap* "Alt")) |
| 158 | + (->> keymap |
137 | 159 | (sort-by first) |
138 | 160 | (map (fn [[command [{:keys [key shift doc]} & [{alternate-key :key}]]]] |
139 | 161 | [:<> |
|
152 | 174 |
|
153 | 175 | (defn ^:dev/after-load render [] |
154 | 176 | (rdom/render [samples] (js/document.getElementById "editor")) |
155 | | - |
156 | 177 | (.. (js/document.querySelectorAll "[clojure-mode]") |
157 | 178 | (forEach #(when-not (.-firstElementChild %) |
158 | 179 | (rdom/render [editor (str/trim (.-innerHTML %))] %)))) |
159 | | - |
160 | 180 | (let [mapping (key-mapping)] |
161 | 181 | (.. (js/document.querySelectorAll ".mod,.alt,.ctrl") |
162 | 182 | (forEach #(when-let [k (get mapping (.-innerHTML %))] |
163 | 183 | (set! (.-innerHTML %) k))))) |
164 | 184 |
|
165 | | - (rdom/render [key-bindings-table] (js/document.getElementById "docs")) |
| 185 | + ;; set viewer tailwind stylesheet |
| 186 | + (j/assoc! (js/document.getElementById "viewer-stylesheet") |
| 187 | + :innerHTML (rc/inline "stylesheets/viewer.css")) |
| 188 | + |
| 189 | + (rdom/render [key-bindings-table (merge keymap/paredit-keymap* (demo.sci/keymap* "Alt"))] (js/document.getElementById "docs")) |
| 190 | + (rdom/render [:div.rounded-md.mb-0.text-sm.monospace.overflow-auto.relative.border.shadow-lg.bg-white |
| 191 | + [markdown-editor {:doc "# Hello Markdown |
| 192 | +
|
| 193 | +Lezer [mounted trees](https://lezer.codemirror.net/docs/ref/#common.MountedTree) allows to |
| 194 | +have an editor with ~~mono~~ _mixed language support_. |
| 195 | +
|
| 196 | +```clojure |
| 197 | +(defn the-answer |
| 198 | + \"to all questions\" |
| 199 | + [] |
| 200 | + (inc 41)) |
| 201 | +``` |
| 202 | +
|
| 203 | +## Todo |
| 204 | +- [x] resolve **inner nodes** |
| 205 | +- [x] fix extra spacing when autoformatting after paredit movements |
| 206 | +- [x] fix errors when entering a newline |
| 207 | +- [ ] fix extra space when entering a newline |
| 208 | +- [x] fix nonsense deletions hitting delete key |
| 209 | +- [x] limit the scope of autoformat (TAB) |
| 210 | +- [x] limit the scope of kill* |
| 211 | +- [x] limit the scope of eval-region |
| 212 | +- [ ] restore autoformat when deleting |
| 213 | +- [x] keep parens balanced when deleting backward |
| 214 | +- [x] fix errors on Ctrl-K |
| 215 | +- [ ] fix dark theme |
| 216 | +- [ ] fix demo error: CssSyntaxError: <css input>:62:15: The `font-inter` class does not exist |
| 217 | +"}]] (js/document.getElementById "markdown-editor")) |
166 | 218 |
|
167 | 219 | (when (linux?) |
168 | 220 | (js/twemoji.parse (.-body js/document)))) |
| 221 | + |
| 222 | +(comment |
| 223 | + (let [ctx' (sci.core/fork @sv/!sci-ctx) |
| 224 | + ctx'' (sci.core/merge-opts ctx' {:namespaces {'foo {'bar "ahoi"}}})] |
| 225 | + |
| 226 | + (demo.sci/eval-string ctx'' "(def o (j/assoc! #js {:a 1} :b 2))") |
| 227 | + (demo.sci/eval-string ctx'' "(j/lookup (j/assoc! #js {:a 1} :b 2))") |
| 228 | + (demo.sci/eval-string ctx'' "(j/get o :b)") |
| 229 | + (demo.sci/eval-string ctx'' "(into-array [1 2 3])") |
| 230 | + |
| 231 | + ;; this is not evaluable as-is in sci |
| 232 | + (demo.sci/eval-string ctx'' "(j/let [^:js {:keys [a b]} o] (map inc [a b]))"))) |
0 commit comments