Skip to content

Commit

Permalink
Support non-evaluated clojure code listings in markdown nextjournal#482
Browse files Browse the repository at this point in the history
By specifying `{:nextjournal.clerk/code-listing true}` after the language.
---------

Co-authored-by: Martin Kavalar <martin@nextjournal.com>
  • Loading branch information
2 people authored and craig-latacora committed Aug 7, 2023
1 parent 4465a93 commit 6b87abd
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 27 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Changes can be:

* 💫 Cache expressions that return `nil` in memory

* 💫 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)).

* 🐜 Turn off analyzer pass for validation of `:type` tags, fixes [#488](https://github.com/nextjournal/clerk/issues/488) @craig-latacora

* 🐜 Strip `:type` metadata from forms before printing them to hash, fixes [#489](https://github.com/nextjournal/clerk/issues/489) @craig-latacora
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ To use Clerk in your project, add the following dependency to your `deps.edn`:

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

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

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

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

```

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

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:

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

```bash
bb dev :browse\? true
bb dev :browse true
```

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

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

## 🐞 Known Issues

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

```
```bibtex
@misc{clerk-github,
author = {Martin Kavalar and
Jack Rusher},
Expand Down
27 changes: 27 additions & 0 deletions notebooks/markdown_fences.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 🤺 Markdown Fences

```
'(evaluated :and "highlighted")
```

```clojure
'(evaluated :and "highlighted")
```

```clojure {:nextjournal.clerk/code-listing true}
'(1 2 "not evaluated" :but-still-highlighted)
```

```clojure {:nextjournal.clerk/code-listing true}
'(1 2 "not evaluated" :but-still-highlighted)
```

```js
() => {
if (true) {
return 'not evaluated'
} else {
return 'what'
}
}
```
1 change: 0 additions & 1 deletion notebooks/readme.clj

This file was deleted.

2 changes: 1 addition & 1 deletion src/nextjournal/clerk/builder.clj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

(def clerk-docs
(into ["CHANGELOG.md"
"README.md"
"notebooks/markdown.md"
"notebooks/onwards.md"]
(map #(str "notebooks/" % ".clj"))
Expand All @@ -37,7 +38,6 @@
"multiviewer"
"pagination"
"paren_soup"
"readme"
"rule_30"
"slideshow"
"visibility"
Expand Down
10 changes: 9 additions & 1 deletion src/nextjournal/clerk/parser.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,20 @@
:nodes (rest nodes)
::md-slice []))

(defn fenced-clojure-code-block? [{:as block :keys [type info language]}]
(and (code? block)
(or (empty? language)
(re-matches #"clj(c?)|clojure" language))
(not (:nextjournal.clerk/code-listing (let [parsed (p/parse-string-all (subs info (count language)))]
(when (n/sexpr-able? parsed)
(n/sexpr parsed)))))))

(defn parse-markdown-string [{:as opts :keys [doc?]} s]
(let [{:as ctx :keys [content]} (parse-markdown (markdown-context) s)]
(loop [{:as state :keys [nodes] ::keys [md-slice]} {:blocks [] ::md-slice [] :nodes content :md-context ctx}]
(if-some [node (first nodes)]
(recur
(if (and (code? node) (contains? node :info))
(if (fenced-clojure-code-block? node)
(-> state
(update :blocks #(cond-> % (seq md-slice) (conj {:type :markdown :doc {:type :doc :content md-slice}})))
(parse-markdown-cell opts))
Expand Down
4 changes: 2 additions & 2 deletions src/nextjournal/clerk/render.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -915,9 +915,9 @@
[:svg {:xmlns "http://www.w3.org/2000/svg" :viewBox "0 0 20 20" :fill "currentColor" :width 12 :height 12}
[: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"}]])

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

(defn render-folded-code-block [code-string {:keys [id]}]
(let [!hidden? (hooks/use-state true)]
Expand Down
21 changes: 13 additions & 8 deletions src/nextjournal/clerk/render/code.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,22 @@
(< pos to)
(concat [(.sliceString text pos to)]))))))))

(defn render-code [^String code _]
(let [builder (RangeSetBuilder.)
_ (highlightTree (.. clojureLanguage -parser (parse code)) highlight-style
(fn [from to style]
(.add builder from to (.mark Decoration (j/obj :class style)))))
decorations-rangeset (.finish builder)
text (.of Text (.split code "\n"))]
(defn lang->deco-range [lang code]
(let [builder (RangeSetBuilder.)]
(when lang
(highlightTree (.. lang -parser (parse code)) highlight-style
(fn [from to style]
(.add builder from to (.mark Decoration (j/obj :class style))))))
(.finish builder)))

(defn render-code [^String code {:keys [language]}]
(let [text (.of Text (.split code "\n"))]
[:div.cm-editor
[:cm-scroller
(into [:div.cm-content.whitespace-pre]
(map (partial style-line decorations-rangeset text))
(map (partial style-line
;; TODO: use-promise hook resolving to language data according to @codemirror/language-data
(lang->deco-range (when (= "clojure" language) clojureLanguage) code) text))
(range 1 (inc (.-lines text))))]]))

;; editable code viewer
Expand Down
15 changes: 8 additions & 7 deletions src/nextjournal/clerk/viewer.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,10 @@
(cond-> []
code?
(conj (with-viewer (if fold? `folded-code-block-viewer `code-block-viewer)
{:nextjournal/opts (merge {:id (processed-block-id (str id "-code"))} (select-keys cell [:loc]))}
{:nextjournal/opts (assoc (select-keys cell [:loc])
:id (processed-block-id (str id "-code"))
:language "clojure")}
(dissoc cell :result)))

(or result? eval?)
(conj (cond-> (ensure-wrapped (-> cell (assoc ::doc doc) (set/rename-keys {:result ::result})))
(and eval? (not result?))
Expand Down Expand Up @@ -704,11 +705,11 @@
{:name :nextjournal.markdown/plain :transform-fn (into-markup [:<>])}
{:name :nextjournal.markdown/ruler :transform-fn (into-markup [:hr])}
{:name :nextjournal.markdown/code
:transform-fn (fn [wrapped-value]
(with-viewer `html-viewer
[:div.code-viewer.code-listing
(with-viewer `code-viewer
(str/trim-newline (md.transform/->text (->value wrapped-value))))]))}
:transform-fn (update-val #(with-viewer `html-viewer
[:div.code-viewer.code-listing
(with-viewer `code-viewer
{:nextjournal/opts (select-keys % [:language])}
(str/trim-newline (md.transform/->text %)))]))}

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

0 comments on commit 6b87abd

Please sign in to comment.