Skip to content

Commit 6bbda19

Browse files
zampinomk
andauthored
Support non-evaluated clojure code listings in markdown #482
By specifying `{:nextjournal.clerk/code-listing true}` after the language. --------- Co-authored-by: Martin Kavalar <[email protected]>
1 parent ed2b537 commit 6bbda19

File tree

9 files changed

+68
-27
lines changed

9 files changed

+68
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Changes can be:
4040

4141
* 💫 Cache expressions that return `nil` in memory
4242

43+
* 💫 Support non-evaluated clojure code listings in markdown documents by specifying `{:nextjournal.clerk/code-listing true}` after the language ([#482](https://github.com/nextjournal/clerk/issues/482)).
44+
4345
* 🐜 Turn off analyzer pass for validation of `:type` tags, fixes [#488](https://github.com/nextjournal/clerk/issues/488) @craig-latacora
4446

4547
* 🐜 Strip `:type` metadata from forms before printing them to hash, fixes [#489](https://github.com/nextjournal/clerk/issues/489) @craig-latacora

README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ To use Clerk in your project, add the following dependency to your `deps.edn`:
5050

5151
Require and start Clerk as part of your system start, e.g. in `user.clj`:
5252

53-
```clojure
53+
```clojure {:nextjournal.clerk/code-listing true}
5454
(require '[nextjournal.clerk :as clerk])
5555

5656
;; start Clerk's built-in webserver on the default port 7777, opening the browser when done
57-
(clerk/serve! {:browse? true})
57+
(clerk/serve! {:browse true})
5858

5959
;; either call `clerk/show!` explicitly
6060
(clerk/show! "notebooks/rule_30.clj")
@@ -68,7 +68,6 @@ Require and start Clerk as part of your system start, e.g. in `user.clj`:
6868
;; Build a html file from the given notebook notebooks.
6969
;; See the docstring for more options.
7070
(clerk/build! {:paths ["notebooks/rule_30.clj"]})
71-
7271
```
7372

7473
You can then access Clerk at <http://localhost:7777>.
@@ -109,7 +108,7 @@ In IntelliJ/Cursive, you can [set up REPL commands](https://cursive-ide.com/user
109108

110109
With [neovim](https://neovim.io/) + [conjure](https://github.com/Olical/conjure/) one can use the following vimscript function to save the file and show it with Clerk:
111110

112-
```
111+
```vimscript
113112
function! ClerkShow()
114113
exe "w"
115114
exe "ConjureEval (nextjournal.clerk/show! \"" . expand("%:p") . "\")"
@@ -122,15 +121,15 @@ nmap <silent> <localleader>cs :execute ClerkShow()<CR>
122121
Make sure you have [Babashka installed](https://github.com/babashka/babashka#installation), and run:
123122

124123
```bash
125-
bb dev :browse\? true
124+
bb dev :browse true
126125
```
127126

128127
The will start everything needed to develop Clerk and open your
129128
default browser. You can connect your favorite editor to it using nREPL.
130129

131130
Any trailing arguments to `bb dev` will be forwarded to `clojure -X`
132131
and `clerk/serve!`. So if you prefer to not open your browser, leave
133-
out the `:browse\? true` arguments.
132+
out the `:browse true` arguments.
134133

135134
## 🐞 Known Issues
136135

@@ -141,7 +140,7 @@ See [notebooks/onwards.md](https://github.com/nextjournal/clerk/blob/main/notebo
141140
If you are a researcher and use Clerk in your work, we encourage you to cite our work.
142141
You can use the following BibTeX citation:
143142

144-
```
143+
```bibtex
145144
@misc{clerk-github,
146145
author = {Martin Kavalar and
147146
Jack Rusher},

notebooks/markdown_fences.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# 🤺 Markdown Fences
2+
3+
```
4+
'(evaluated :and "highlighted")
5+
```
6+
7+
```clojure
8+
'(evaluated :and "highlighted")
9+
```
10+
11+
```clojure {:nextjournal.clerk/code-listing true}
12+
'(1 2 "not evaluated" :but-still-highlighted)
13+
```
14+
15+
```clojure {:nextjournal.clerk/code-listing true}
16+
'(1 2 "not evaluated" :but-still-highlighted)
17+
```
18+
19+
```js
20+
() => {
21+
if (true) {
22+
return 'not evaluated'
23+
} else {
24+
return 'what'
25+
}
26+
}
27+
```

notebooks/readme.clj

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/nextjournal/clerk/builder.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
(def clerk-docs
2020
(into ["CHANGELOG.md"
21+
"README.md"
2122
"notebooks/markdown.md"
2223
"notebooks/onwards.md"]
2324
(map #(str "notebooks/" % ".clj"))
@@ -37,7 +38,6 @@
3738
"multiviewer"
3839
"pagination"
3940
"paren_soup"
40-
"readme"
4141
"rule_30"
4242
"slideshow"
4343
"visibility"

src/nextjournal/clerk/parser.cljc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,20 @@
357357
:nodes (rest nodes)
358358
::md-slice []))
359359

360+
(defn fenced-clojure-code-block? [{:as block :keys [type info language]}]
361+
(and (code? block)
362+
(or (empty? language)
363+
(re-matches #"clj(c?)|clojure" language))
364+
(not (:nextjournal.clerk/code-listing (let [parsed (p/parse-string-all (subs info (count language)))]
365+
(when (n/sexpr-able? parsed)
366+
(n/sexpr parsed)))))))
367+
360368
(defn parse-markdown-string [{:as opts :keys [doc?]} s]
361369
(let [{:as ctx :keys [content]} (parse-markdown (markdown-context) s)]
362370
(loop [{:as state :keys [nodes] ::keys [md-slice]} {:blocks [] ::md-slice [] :nodes content :md-context ctx}]
363371
(if-some [node (first nodes)]
364372
(recur
365-
(if (and (code? node) (contains? node :info))
373+
(if (fenced-clojure-code-block? node)
366374
(-> state
367375
(update :blocks #(cond-> % (seq md-slice) (conj {:type :markdown :doc {:type :doc :content md-slice}})))
368376
(parse-markdown-cell opts))

src/nextjournal/clerk/render.cljs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -915,9 +915,9 @@
915915
[:svg {:xmlns "http://www.w3.org/2000/svg" :viewBox "0 0 20 20" :fill "currentColor" :width 12 :height 12}
916916
[:path {:fill-rule "evenodd" :d "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" :clip-rule "evenodd"}]])
917917

918-
(defn render-code-block [code-string {:keys [id]}]
918+
(defn render-code-block [code-string {:as opts :keys [id]}]
919919
[:div.viewer.code-viewer.w-full.max-w-wide {:data-block-id id}
920-
[code/render-code code-string]])
920+
[code/render-code code-string opts]])
921921

922922
(defn render-folded-code-block [code-string {:keys [id]}]
923923
(let [!hidden? (hooks/use-state true)]

src/nextjournal/clerk/render/code.cljs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,22 @@
8787
(< pos to)
8888
(concat [(.sliceString text pos to)]))))))))
8989

90-
(defn render-code [^String code _]
91-
(let [builder (RangeSetBuilder.)
92-
_ (highlightTree (.. clojureLanguage -parser (parse code)) highlight-style
93-
(fn [from to style]
94-
(.add builder from to (.mark Decoration (j/obj :class style)))))
95-
decorations-rangeset (.finish builder)
96-
text (.of Text (.split code "\n"))]
90+
(defn lang->deco-range [lang code]
91+
(let [builder (RangeSetBuilder.)]
92+
(when lang
93+
(highlightTree (.. lang -parser (parse code)) highlight-style
94+
(fn [from to style]
95+
(.add builder from to (.mark Decoration (j/obj :class style))))))
96+
(.finish builder)))
97+
98+
(defn render-code [^String code {:keys [language]}]
99+
(let [text (.of Text (.split code "\n"))]
97100
[:div.cm-editor
98101
[:cm-scroller
99102
(into [:div.cm-content.whitespace-pre]
100-
(map (partial style-line decorations-rangeset text))
103+
(map (partial style-line
104+
;; TODO: use-promise hook resolving to language data according to @codemirror/language-data
105+
(lang->deco-range (when (= "clojure" language) clojureLanguage) code) text))
101106
(range 1 (inc (.-lines text))))]]))
102107

103108
;; editable code viewer

src/nextjournal/clerk/viewer.cljc

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -597,9 +597,10 @@
597597
(cond-> []
598598
code?
599599
(conj (with-viewer (if fold? `folded-code-block-viewer `code-block-viewer)
600-
{:nextjournal/opts (merge {:id (processed-block-id (str id "-code"))} (select-keys cell [:loc]))}
600+
{:nextjournal/opts (assoc (select-keys cell [:loc])
601+
:id (processed-block-id (str id "-code"))
602+
:language "clojure")}
601603
(dissoc cell :result)))
602-
603604
(or result? eval?)
604605
(conj (cond-> (ensure-wrapped (-> cell (assoc ::doc doc) (set/rename-keys {:result ::result})))
605606
(and eval? (not result?))
@@ -704,11 +705,11 @@
704705
{:name :nextjournal.markdown/plain :transform-fn (into-markup [:<>])}
705706
{:name :nextjournal.markdown/ruler :transform-fn (into-markup [:hr])}
706707
{:name :nextjournal.markdown/code
707-
:transform-fn (fn [wrapped-value]
708-
(with-viewer `html-viewer
709-
[:div.code-viewer.code-listing
710-
(with-viewer `code-viewer
711-
(str/trim-newline (md.transform/->text (->value wrapped-value))))]))}
708+
:transform-fn (update-val #(with-viewer `html-viewer
709+
[:div.code-viewer.code-listing
710+
(with-viewer `code-viewer
711+
{:nextjournal/opts (select-keys % [:language])}
712+
(str/trim-newline (md.transform/->text %)))]))}
712713

713714
;; marks
714715
{:name :nextjournal.markdown/em :transform-fn (into-markup [:em])}

0 commit comments

Comments
 (0)