A content/reference/core/generator.nuj => content/reference/core/generator.nuj +99 -0
@@ 0,0 1,99 @@
+[import [pp-nujel] :pretty/nujel]
+
+[defn closure-signature [f]
+ [def args ""]
+ [def arg-i 0]
+ [def arguments [closure/arguments f]]
+ [while arguments
+ [if [pair? arguments]
+ [when [car arguments] [set! args [fmt "{args} <span class=\"argument\" arg-i=\"{}\">{}</span>" [inc! arg-i] [car arguments]]]]
+ [set! args [fmt "{args} <span class=\"argument rest-argument\" arg-i=\"{}\">{}</span>" [inc! arg-i] arguments]]]
+ [cdr! arguments]]
+ [fmt "<div class=\"signature\"><h1>{}</h1>{}</div>" [closure/name f] args]]
+
+[defn closure-html-arg [f arg]
+ [def i [index-of arg ":"]]
+ [when [< i 0] [return arg]]
+ [return [fmt "<div class=\"docstring-arg-name\">{}</div><div class=\"docstring-arg-desc\">{}</div>" [string/cut arg 0 i] [color-args f [string/cut arg [inc i]]]]]]
+
+[defn closure-html-arguments [f args]
+ [def i 0]
+ [fmt "<div class=\"docstring-args-wrap\">{}</div>"
+ [join [map [split args "\n"]
+ [fn [arg]
+ [fmt "<div class=\"docstring-arg\" arg-i=\"{}\">{}</div>" [inc! i] [closure-html-arg f arg]]]] ""]]]
+
+[defn closure-html-return-val [f v]
+ [fmt "<div class=\"docstring-return-val\">{}</div>" [color-args f v]]]
+
+[defn color-args [f text]
+ [def i 0]
+ [def args [closure/arguments f]]
+ [while args
+ [inc! i]
+ [if [pair? args]
+ [def arg [car args]]
+ [def arg args]]
+ [set! text [join [split text [string arg]] [fmt "<span class=\"arg-ref\" arg-i=\"{}\">{arg}</span>" i]]]
+ [cdr! args]]
+ [return text]]
+
+[defn closure-html-documentation [f]
+ [def raw [closure/documentation f]]
+ [when-not raw [return "<p>No documentation available</p>"]]
+ [def parts [split raw "\n\n"]]
+ [def ret [fmt "<h4>Summary</h4><p class=\"docstring-intro\">{}</p>" [color-args f [car parts]]]]
+ [cdr! parts]
+ [def len [length parts]]
+ [when [> len 2]
+ [set! ret [fmt "{ret}<h4>Description</h4>"]]]
+ [while [> len 2]
+ [set! ret [fmt "{ret}<p class=\"docstring-desc\">{}</p>" [color-args f [car parts]]]]
+ [cdr! parts]
+ [dec! len]]
+ [when [>= len 2]
+ [set! ret [fmt "{ret}<h4>Arguments</h4>{}" [closure-html-arguments f [car parts]]]]
+ [cdr! parts]
+ [dec! len]]
+ [when [>= len 1]
+ [set! ret [fmt "{ret}<h4>Return value</h4>{}" [closure-html-return-val f [car parts]]]]
+ [cdr! parts]
+ [dec! len]]
+ [return ret]]
+
+[defn closure-html-examples [f]
+ [def examples [meta f :tests]]
+ [when examples
+ [cat "<h4>Examples</h4><pre class=\"docstring-examples source source-nujel\">"
+ [-> examples
+ [map [fn [e] [pp-nujel [fmt "{:?}\n{:?}\n\n" [cadr e] [car e]] :html]]]
+ [join ""]]
+ "</pre>"]]]
+
+[defn closure-html-related [f]
+ [def rel [meta f :related]]
+ [when rel
+ [cat "<h4>Related</h4><div class=\"docstring-related-wrap\">"
+ [-> rel
+ [map [fn [e] [fmt "{} " e]]]
+ [join ""]]
+ "</div>"]]]
+
+[defn generate-reference-for [f]
+ [cat [closure-signature f]
+ "<br/><br/>"
+ [closure-html-documentation f]
+ [closure-html-examples f]
+ [closure-html-related f]]]
+
+[defn generate-content-for [f]
+ [def filename [fmt "{}.html" [closure/name f]]]
+ [when [= filename ".html"] [return #nil]]
+ [queue-content filename
+ @[:type :page :title [closure/name f] :category [closure/cat f]]
+ [fn [] [generate-reference-for f]]]]
+
+[-> [symbol-table]
+ [map resolve]
+ [filter callable?]
+ [for-each generate-content-for]]
M content/reference/index.html => content/reference/index.html +17 -4
@@ 5,8 5,21 @@ date: "2022-09-13"
+++
<h1>The Nujel reference</h1>
-<p>This is an experiment in generating a usable reference using the builtin reflection abilities, they will be pretty terrible at first, but hopefully will improve over time in quality.</p>
+<p>
+ This is an experiment in generating a usable reference using the builtin reflection abilities,
+ they will be pretty terrible at first, but hopefully will improve over time in quality.
+</p>
+<p>
+ The ones that are categorized should hopefully have some passable documentation attached, the unsorted rest will hopefully shrink over time as I document the standard library.
+</p>
+<p>
+ All the documentation here is generated by parsing the built-in documentation and some additional metadata. All the examples seen here are also just pretty printed unit tests which are run on every commit.
+</p>
-<nav>
- {{ [join [sort [map [navigation 2 "reference/"] [fn [v] [render-link v]]]] "<br/>"] }}
-</nav>>
\ No newline at end of file
+{{
+ [def cats @[]]
+ [-> [symbol-table]
+ [map resolve]
+ [for-each [fn [a] [tree/set! cats [closure/cat a] #t]]]]
+ [join [map [tree/keys cats] [fn [a] [component :CategoryNavigation @[:category a]]]] "<br/><br/>"]
+}}
M main.nuj => main.nuj +9 -4
@@ 4,16 4,21 @@
;;; Most functionality is separated into modules that can be found under /ssg/
[import [rainbow] :ansi]
-[import [get :as context/get] "ssg/context"]
-[import [build :as content/build add-resources] "ssg/content"]
+[import [get :as context-get] "ssg/context"]
+[import [build-ctx build-queued generate-content build-frontmatter-index add-resources] "ssg/content"]
[import [load-builtin-themes load-components] "ssg/theme"]
+[import [build-prev-next-list] "ssg/navigation"]
[defn main [args]
:export
[pfmtln "Welcome to the {} SSG" [rainbow "Nujel"]]
[when [and [car args] [string? [car args]]] [cd [car args]]]
- [-> [context/get "./"]
+ [-> [context-get "./"]
load-builtin-themes
load-components
- content/build
+ build-frontmatter-index
+ generate-content
+ build-prev-next-list
+ build-ctx
+ build-queued
add-resources]]
M ssg/content.nuj => ssg/content.nuj +45 -11
@@ 1,6 1,5 @@
-[import [build :as loader/build] "loader"]
-[import [parse-frontmatter frontmatter get-frontmatter] "theme"]
-[import [build-prev-next-list] "navigation"]
+[import [build :as loader/build mkdir-safe get-out-path] "loader"]
+[import [render parse-frontmatter frontmatter get-frontmatter] "theme"]
[defn add-resources [ctx]
:export
@@ 21,13 20,37 @@
[defn build-content [ctx path content-frontmatter build-fun]
[when [file/file? [cat path "/config.nuj"]]
[set! ctx [tree/merge ctx [file/eval [cat path "/config.nuj"]]]]]
- [doseq [c [directory/read-relative path] content-frontmatter]
+ [doseq [c [directory/read-relative path] ctx]
[if [file/dir? c]
[build-content ctx c content-frontmatter build-fun]
[build-fun ctx c content-frontmatter]]]]
-[defn build [ctx]
- "Build everything"
+[defn queue-content* [ctx base-path out-path fm content-fun]
+ [def path [cat base-path out-path]]
+ [def href [string/cut path [inc [length [tree/ref ctx :content-root-dir]]]]]
+ [tree/set! fm :href href]
+ [tree/set! fm :depth [- [length [split [tree/ref fm :href] "/"]] 1]]
+ [tree/set! [tree/ref ctx :frontmatter] [string->keyword href] fm]
+ [tree/set! [tree/ref ctx :generator-queue] [string->keyword href] @[:path path :meta fm :fun content-fun]]]
+
+
+[defn generate-content [ctx]
+ "Generate all autogenerated content"
+ :export
+ [build-content ctx
+ [tree/ref ctx :content-root-dir]
+ [tree/ref ctx :frontmatter]
+ [fn [ctx path content-frontmatter]
+ [when-not [== [path/basename path] "generator.nuj"]
+ [return #nil]]
+ [def base-path [cat [path/dirname path] "/"]]
+ [defn queue-content [out-path fm content-fun]
+ [queue-content* ctx base-path out-path fm content-fun]]
+ [file/eval path [current-closure]]]]]
+
+
+[defn build-frontmatter-index [ctx]
+ "Load all the frontmatters and look generate datastructures necessary for building navigations"
:export
[def content-frontmatter @[]]
[build-content ctx
@@ 37,10 60,21 @@
[load-frontmatter path
[string/cut path [inc [length [tree/ref ctx :content-root-dir]]]]
content-frontmatter]]]
- [tree/set! ctx :frontmatter content-frontmatter]
- [build-prev-next-list ctx]
+ [tree/set! ctx :frontmatter content-frontmatter]]
+
+[defn build-ctx [ctx]
+ "Build everything"
+ :export
[build-content ctx
[tree/ref ctx :content-root-dir]
- content-frontmatter
- loader/build]
- [return ctx]]
+ [tree/ref ctx :frontmatter]
+ loader/build]]
+
+[defn build-queued [ctx]
+ "Build queued generator content"
+ :export
+ [doseq [d [tree/values [tree/ref ctx :generator-queue]] ctx]
+ [def raw-content [[tree/ref d :fun]]]
+ [def path [get-out-path ctx [tree/ref d :path]]]
+ [mkdir-safe [path/dirname path]]
+ [spit path [render ctx path [tree/ref d :meta] raw-content]]]]
M ssg/context.nuj => ssg/context.nuj +1 -1
@@ 12,4 12,4 @@
:templates @[]
:resources-needed @[]
- :loaders @[]]]
+ :generator-queue @[]]]
M ssg/loader.nuj => ssg/loader.nuj +10 -3
@@ 1,10 1,12 @@
-[import [render] "theme"]
+[import [render split-frontmatter get-frontmatter] "theme"]
[defn get-out-path [ctx path]
+ :export
[cat [tree/ref ctx :deploy-dir]
[string/cut path [length [tree/ref ctx :content-root-dir]]]]]
[defn mkdir-safe [full-path]
+ :export
[def parts [split full-path "/"]]
[def path #nil]
[while parts
@@ 15,10 17,15 @@
[mkdir path]]]
[defn build-html [ctx path]
- "Loader that just copies the file over"
+ "Loader that renders a HTML template"
+ :export
[def dest-path [get-out-path ctx path]]
[mkdir-safe [path/dirname dest-path]]
- [spit dest-path [render ctx path]]
+ [def page-parts [split-frontmatter [slurp path]]]
+ [def meta [get-frontmatter path
+ [string/cut path [inc [length [tree/ref ctx :content-root-dir]]]]
+ [car page-parts]]]
+ [spit dest-path [render ctx path meta [cdr page-parts]]]
[return dest-path]]
[defn build-copy [ctx path]
M ssg/navigation.nuj => ssg/navigation.nuj +3 -1
@@ 22,7 22,7 @@
[cdr! c]]
[tree/set! ctx :prev-next-nav nav]]
-[defn build [ctx depth prefix]
+[defn build [ctx depth prefix category]
"Build up a navigation and return a list of trees, describing the entries"
:export
[def l [-> [tree/values [tree/ref ctx :frontmatter]]
@@ 31,5 31,7 @@
[set! l [filter l [fn [a] [== depth [tree/ref a :depth]]]]]]
[when prefix
[set! l [filter l [fn [a] [== prefix [cut [tree/ref a :href] 0 [buffer/length prefix]]]]]]]
+ [when category
+ [set! l [filter l [fn [a] [== category [tree/ref a :category]]]]]]
[list/sort l [fn [a b] [< [tree/ref a :href]
[tree/ref b :href]]]]]
M ssg/theme.nuj => ssg/theme.nuj +15 -9
@@ 115,13 115,23 @@
[defn set-component-path! [new-path]
[set! *cur-component-path* new-path]]
+[defn urlescape [in]
+ [with-string-port out
+ [dotimes [i [buffer/length in]]
+ [def c [buffer/ref in i]]
+ [case c
+ [#\# [out 'block-write "%23"]]
+ [#\& [out 'block-write "%26"]]
+ [#\? [out 'block-write "%3F"]]
+ [otherwise [out 'char-write c]]]]]]
+
[defn get-href* [ctx path target]
[def name [string/cut path [inc [length [tree/ref ctx :content-root-dir]]]]]
[def depth [- [length [split name "/"]] 1]]
[def href [if [tree? target]
[tree/ref target :href]
target]]
- [dotimes [i depth href]
+ [dotimes [i depth [urlescape href]]
[set! href [cat "../" href]]]]
[defn render-link* [ctx path target]
@@ 140,8 150,8 @@
[defn get-href [target]
[get-href* [get-ctx] [get-path] target]]
-[defn navigation [depth prefix]
- [navigation/build [get-ctx] depth prefix]]
+[defn navigation [depth prefix category]
+ [navigation/build [get-ctx] depth prefix category]]
[defn get-posts []
[def ret #nil]
@@ 208,14 218,10 @@
[set-component-path! old-component-path]
[return ret]]]]
-[defn render [ctx path]
+[defn render [ctx path meta raw-content]
:export
- [def page-parts [split-frontmatter [slurp path]]]
- [def meta [get-frontmatter path
- [string/cut path [inc [length [tree/ref ctx :content-root-dir]]]]
- [car page-parts]]]
[set-page-ctx! ctx path meta]
- [def content [parse-content ctx path [cdr page-parts] meta]]
+ [def content [parse-content ctx path raw-content meta]]
[def ret [template [tree/ref [get-meta] :type] meta content]]
[reset-page-ctx!]
[return ret]]
A ssg/theme/default/components/CategoryNavigation.html => ssg/theme/default/components/CategoryNavigation.html +4 -0
@@ 0,0 1,4 @@
+<h2>{{ [def cur-cat [tree/ref props :category]] [keyword->string cur-cat] }}</h2>
+<nav>
+{{ [join [sort [map [navigation #nil #nil cur-cat] [fn [v] [render-link v]]]] "<br/>"] }}
+</nav><
\ No newline at end of file
M ssg/theme/default/resources/main.css => ssg/theme/default/resources/main.css +94 -1
@@ 373,4 373,97 @@ h6 a{
.nujel-hl-12 {color:#528bff;}
.nujel-hl-13 {color:#7e0097;}
.nujel-hl-14 {color:#56b6c2;}
-.nujel-hl-15 {color:#d7dae0; font-weight: bold;}>
\ No newline at end of file
+.nujel-hl-15 {color:#d7dae0; font-weight: bold;}
+
+.signature {
+ display: inline-block;
+ font-size: 1.4rem;
+}
+.signature::before {
+ content:"[";
+ color:#d7dae0;
+}
+.signature::after {
+ content:"]";
+ color:#d7dae0;
+}
+
+.signature h1 {
+ display: inline;
+ padding: 0;
+ margin: 0;
+ font-size: inherit;
+ font-weight: normal;
+ font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
+}
+
+.signature > .argument {
+ display:inline;
+ padding:0;
+ margin:0;
+ font-size:1.4rem;
+}
+.signature > .argument.rest-argument::before {
+ content:' . ';
+ color:#d7dae0;
+}
+
+.signature > .argument[arg-i="1"] {color:#56b6c2;}
+.signature > .argument[arg-i="2"] {color:#98c379;}
+.signature > .argument[arg-i="3"] {color:#d5b06b;}
+.signature > .argument[arg-i="4"] {color:#e06c75;}
+.signature > .argument[arg-i="5"] {color:#c678dd;}
+.signature > .argument[arg-i="6"] {color:#528bff;}
+
+.docstring-intro {
+ margin-bottom:2em;
+}
+
+.docstring-desc {
+ margin-bottom: 1em;
+}
+
+.docstring-arg-name {
+ display: block;
+}
+.docstring-arg-desc {
+ display: inline-block;
+ padding-left:1em;
+}
+.docstring-arg {
+ margin-bottom: 1em;
+}
+.docstring-arg:last-child {
+ margin-bottom: 0;
+}
+
+.docstring-return-val,
+.docstring-args-wrap {
+ margin-bottom: 2em;
+}
+
+.docstring-arg[arg-i="1"] .docstring-arg-name {color:#56b6c2;}
+.docstring-arg[arg-i="2"] .docstring-arg-name {color:#98c379;}
+.docstring-arg[arg-i="3"] .docstring-arg-name {color:#d5b06b;}
+.docstring-arg[arg-i="4"] .docstring-arg-name {color:#e06c75;}
+.docstring-arg[arg-i="5"] .docstring-arg-name {color:#c678dd;}
+.docstring-arg[arg-i="6"] .docstring-arg-name {color:#528bff;}
+
+.arg-ref[arg-i="1"] {color:#56b6c2;}
+.arg-ref[arg-i="2"] {color:#98c379;}
+.arg-ref[arg-i="3"] {color:#d5b06b;}
+.arg-ref[arg-i="4"] {color:#e06c75;}
+.arg-ref[arg-i="5"] {color:#c678dd;}
+.arg-ref[arg-i="6"] {color:#528bff;}
+
+p {
+ margin: 0 0 1em 0;
+ font-size: 1rem;
+ line-height: 1.35em;
+}
+
+h4 {
+ color: #8f949e;
+ font-size: 1.4rem;
+ margin-bottom: 0.25em;
+}<
\ No newline at end of file