├── .gitignore ├── .travis.yml ├── README.md ├── codex-templates.asd ├── codex-test.asd ├── codex.asd ├── docs ├── authoring-templates.scr ├── gamma.png ├── includes │ ├── docstring.lisp │ ├── formatting.scr │ ├── manifest.lisp │ ├── methods.scr │ ├── nodes.scr │ ├── overview.scr │ └── spec.scr ├── internals.scr ├── manifest.lisp ├── minima.png ├── overview.scr ├── templates.scr └── tutorial.scr ├── roswell └── codex.ros ├── src ├── codex.lisp ├── error.lisp ├── macro.lisp ├── manifest.lisp ├── markup.lisp └── quickstart.lisp ├── t ├── codex.lisp ├── manifests │ ├── output.lisp │ └── template.lisp └── test-system │ ├── codex-test-system.asd │ ├── docs │ ├── manifest.lisp │ ├── sec-a.scr │ ├── sec-b.scr │ └── test.png │ └── test-system.lisp └── templates ├── gamma ├── base.html ├── document.html ├── section.html └── style.css ├── minima ├── base.html ├── document.html ├── section.html └── style.css ├── static ├── highlight-lisp │ ├── README.md │ ├── highlight-lisp.js │ └── themes │ │ ├── dark.css │ │ ├── github.css │ │ ├── vestigial.css │ │ └── wookie.css ├── mathjax │ ├── load-mathjax.js │ ├── tex-chtml.js │ └── woff-v2 │ │ ├── MathJax_AMS-Regular.woff │ │ ├── MathJax_Calligraphic-Bold.woff │ │ ├── MathJax_Calligraphic-Regular.woff │ │ ├── MathJax_Fraktur-Bold.woff │ │ ├── MathJax_Fraktur-Regular.woff │ │ ├── MathJax_Main-Bold.woff │ │ ├── MathJax_Main-Italic.woff │ │ ├── MathJax_Main-Regular.woff │ │ ├── MathJax_Math-BoldItalic.woff │ │ ├── MathJax_Math-Italic.woff │ │ ├── MathJax_Math-Regular.woff │ │ ├── MathJax_SansSerif-Bold.woff │ │ ├── MathJax_SansSerif-Italic.woff │ │ ├── MathJax_SansSerif-Regular.woff │ │ ├── MathJax_Script-Regular.woff │ │ ├── MathJax_Size1-Regular.woff │ │ ├── MathJax_Size2-Regular.woff │ │ ├── MathJax_Size3-Regular.woff │ │ ├── MathJax_Size4-Regular.woff │ │ ├── MathJax_Typewriter-Regular.woff │ │ ├── MathJax_Vector-Bold.woff │ │ ├── MathJax_Vector-Regular.woff │ │ └── MathJax_Zero.woff ├── nodes.css └── reset.css └── templates.lisp /.gitignore: -------------------------------------------------------------------------------- 1 | *.fasl 2 | *.dx32fsl 3 | *.dx64fsl 4 | *.lx32fsl 5 | *.lx64fsl 6 | *.x86f 7 | *~ 8 | .#* 9 | 10 | docs/build 11 | t/test-system/docs/build 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: common-lisp 2 | 3 | env: 4 | global: 5 | - PATH=~/.roswell/bin:$PATH 6 | - ROSWELL_BRANCH=master 7 | - ROSWELL_INSTALL_DIR=$HOME/.roswell 8 | - COVERAGE_EXCLUDE=t 9 | matrix: 10 | - LISP=sbcl-bin COVERALLS=true 11 | 12 | install: 13 | # Roswell & coveralls 14 | - curl -L https://raw.githubusercontent.com/snmsts/roswell/$ROSWELL_BRANCH/scripts/install-for-ci.sh | sh 15 | - git clone https://github.com/fukamachi/cl-coveralls ~/lisp/cl-coveralls 16 | # Clone some libraries 17 | - git clone https://github.com/CommonDoc/common-doc.git ~/lisp/common-doc 18 | - git clone https://github.com/CommonDoc/common-doc-plump.git ~/lisp/common-doc-plump 19 | - git clone https://github.com/CommonDoc/common-html.git ~/lisp/common-html 20 | - git clone https://github.com/CommonDoc/vertex.git ~/lisp/vertex 21 | - git clone https://github.com/CommonDoc/scriba.git ~/lisp/scriba 22 | - git clone https://github.com/CommonDoc/parenml.git ~/lisp/parenml 23 | - git clone https://github.com/CommonDoc/pandocl.git ~/lisp/pandocl 24 | - git clone https://github.com/mmontone/djula.git ~/lisp/djula 25 | - git clone https://github.com/eudoxia0/docparser.git ~/lisp/docparser 26 | 27 | cache: 28 | directories: 29 | - $HOME/.roswell 30 | - $HOME/.config/common-lisp 31 | 32 | before_script: 33 | - ros --version 34 | - ros config 35 | 36 | script: 37 | - ros -e '(ql:quickload (list :fiveam :cl-coveralls))' 38 | -e '(setf fiveam:*debug-on-error* t 39 | fiveam:*debug-on-failure* t)' 40 | -e '(setf *debugger-hook* 41 | (lambda (c h) 42 | (declare (ignore c h)) 43 | (uiop:quit -1)))' 44 | -e '(coveralls:with-coveralls (:exclude (list "t" "src/error.lisp")) 45 | (ql:quickload :codex-templates) 46 | (ql:quickload :codex-test))' 47 | 48 | notifications: 49 | email: 50 | - eudoxiahp@gmail.com 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Codex 2 | 3 | [![Build Status](https://travis-ci.org/CommonDoc/codex.svg?branch=master)](https://travis-ci.org/CommonDoc/codex) 4 | [![Coverage Status](https://coveralls.io/repos/CommonDoc/codex/badge.svg?branch=master)](https://coveralls.io/r/CommonDoc/codex?branch=master) 5 | 6 | A documentation system for Common Lisp. 7 | 8 | # Overview 9 | 10 | **Codex** generates documentation. You just write docstrings as usual, only 11 | using [Scriba][scriba] syntax, append a couple of extra files (A few tutorials 12 | and what have you), and Codex creates beautiful online documentation, and in the 13 | near future, PDF manuals. 14 | 15 | See the [documentation][docs] 16 | 17 | # License 18 | 19 | Copyright (c) 2014-2015 Fernando Borretti 20 | 21 | Licensed under the MIT License. 22 | 23 | [scriba]: https://github.com/CommonDoc/scriba 24 | [docs]: http://commondoc.github.io/codex/docs/overview.html 25 | -------------------------------------------------------------------------------- /codex-templates.asd: -------------------------------------------------------------------------------- 1 | (defsystem codex-templates 2 | :author "Fernando Borretti " 3 | :maintainer "Fernando Borretti " 4 | :license "MIT" 5 | :version "0.1" 6 | :depends-on (:common-html 7 | :djula 8 | :cl-fad 9 | :trivial-types) 10 | :components ((:module "templates" 11 | :serial t 12 | :components 13 | ((:file "templates") 14 | (:module "static" 15 | :components 16 | ((:static-file "nodes.css") 17 | (:static-file "reset.css"))) 18 | (:module "minima" 19 | :components 20 | ((:static-file "base.html") 21 | (:static-file "document.html") 22 | (:static-file "section.html") 23 | (:static-file "style.css"))) 24 | (:module "gamma" 25 | :components 26 | ((:static-file "base.html") 27 | (:static-file "document.html") 28 | (:static-file "section.html") 29 | (:static-file "style.css"))) 30 | (:module "traditional" 31 | :components 32 | ((:static-file "base.html") 33 | (:static-file "document.html") 34 | (:static-file "section.html") 35 | (:static-file "style.css")))))) 36 | :description "Templates for Codex.") 37 | -------------------------------------------------------------------------------- /codex-test.asd: -------------------------------------------------------------------------------- 1 | (defsystem codex-test 2 | :author "Fernando Borretti " 3 | :license "MIT" 4 | :description "Tests for Codex." 5 | :depends-on (:codex 6 | :vertex 7 | :cffi 8 | :common-html 9 | :fiveam) 10 | :components ((:module "t" 11 | :serial t 12 | :components 13 | ((:module "manifests" 14 | :components 15 | ((:static-file "output.lisp") 16 | (:static-file "template.lisp"))) 17 | (:module "test-system" 18 | :components 19 | ((:static-file "codex-test-system.asd") 20 | (:static-file "test-system.lisp") 21 | (:module "docs" 22 | :components 23 | ((:static-file "manifest.lisp") 24 | (:static-file "sec-a.scr") 25 | (:static-file "sec-b.scr"))))) 26 | (:file "codex"))))) 27 | -------------------------------------------------------------------------------- /codex.asd: -------------------------------------------------------------------------------- 1 | (defsystem codex 2 | :author "Fernando Borretti " 3 | :maintainer "Fernando Borretti " 4 | :license "MIT" 5 | :version "0.2" 6 | :homepage "https://github.com/CommonDoc/codex" 7 | :bug-tracker "https://github.com/CommonDoc/codex/issues" 8 | :source-control (:git "git@github.com:CommonDoc/codex.git") 9 | :depends-on (:docparser 10 | :common-doc 11 | :common-doc-contrib 12 | :pandocl 13 | :codex-templates 14 | :cl-slug 15 | :cl-ppcre 16 | :alexandria) 17 | :components ((:module "src" 18 | :serial t 19 | :components 20 | ((:file "error") 21 | (:file "markup") 22 | (:file "manifest") 23 | (:file "macro") 24 | (:file "codex") 25 | (:file "quickstart")))) 26 | :description "A documentation system for Common Lisp." 27 | :long-description 28 | #.(uiop:read-file-string 29 | (uiop:subpathname *load-pathname* "README.md")) 30 | :in-order-to ((test-op (test-op codex-test)))) 31 | -------------------------------------------------------------------------------- /docs/authoring-templates.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Authoring Templates) 3 | 4 | @begin(section) 5 | @title(HTML Classes) 6 | 7 | The following is a list of HTML classes in the documentation nodes inserted into 8 | the documents. Some of these (e.g., @c(codex-slot) and @c(codex-class-slot)) are 9 | used simultaneously. 10 | 11 | @begin(deflist) 12 | 13 | @term(@c(codex-name)) 14 | @def(The name of a node, e.g. a function or a class.) 15 | 16 | @term(@c(codex-docstring)) 17 | @def(The docstring section of a node.) 18 | 19 | @term(@c(codex-doc-node)) 20 | @def(A documentation node.) 21 | 22 | @term(@c(codex-lambda-list)) 23 | @def(The lambda list of an operator node.) 24 | 25 | @term(@c(codex-function)) 26 | @def(A function node.) 27 | 28 | @term(@c(codex-macro)) 29 | @def(A macro node.) 30 | 31 | @term(@c(codex-generic-function)) 32 | @def(A generic function node.) 33 | 34 | @term(@c(codex-method)) 35 | @def(A method node.) 36 | 37 | @term(@c(codex-operator)) 38 | @def(This class is present in all operator nodes. If an unsupported subclass of 39 | @c(operator-node) is used, it expands the name, docstring and lambda list and 40 | nothing more, and only uses this class.) 41 | 42 | @term(@c(codex-slot)) 43 | @def(A structure or class slot.) 44 | 45 | @term(@c(codex-structure-slot)) 46 | @def(The slot of a structure. This only contains a name, not a docstring.) 47 | 48 | @term(@c(codex-class-slot)) 49 | @def(The slot of a class.) 50 | 51 | @term(@c(codex-record)) 52 | @def(A structure or class node.) 53 | 54 | @term(@c(codex-structure)) 55 | @def(A structure node.) 56 | 57 | @term(@c(codex-class)) 58 | @def(A class node.) 59 | 60 | @term(@c(codex-condition)) 61 | @def(A condition node.) 62 | 63 | @term(@c(codex-variable)) 64 | @def(A variable node.) 65 | 66 | @term(@c(codex-type)) 67 | @def(A type definition node.) 68 | 69 | @term(@c(codex-type-def)) 70 | @def(The actual type definition in the type node.) 71 | 72 | @end(deflist) 73 | 74 | @end(section) 75 | 76 | @end(section) 77 | -------------------------------------------------------------------------------- /docs/gamma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/docs/gamma.png -------------------------------------------------------------------------------- /docs/includes/docstring.lisp: -------------------------------------------------------------------------------- 1 | (defun download-website-text (url) 2 | "Downloads @cl:param(url) and strips all HTML tags." 3 | ...) 4 | 5 | (defclass metal () 6 | ((cost :reader cost 7 | :initarg cost 8 | :type float 9 | :documentation "All instances @b(must) initialize cost to a floating 10 | point value."))) 11 | -------------------------------------------------------------------------------- /docs/includes/formatting.scr: -------------------------------------------------------------------------------- 1 | This text is @b(bold), this is @i(italics), and this is @c(code). You might also 2 | want @u(underlined) text. 3 | 4 | The speed of light is 3x10@sup(8) meters per second. The symbol for the 5 | permittivity of vacuum is ε@sub(0). 6 | -------------------------------------------------------------------------------- /docs/includes/manifest.lisp: -------------------------------------------------------------------------------- 1 | (:docstring-markup-format :scriba 2 | :systems (:my-library) 3 | :documents ((:title "My Library" 4 | :authors ("John Q. Lisper") 5 | :output-format (:type :multi-html 6 | :template :minima) 7 | :sources ("manual.scr")))) 8 | -------------------------------------------------------------------------------- /docs/includes/methods.scr: -------------------------------------------------------------------------------- 1 | @cl:with-package[name="serialize-lib"]( 2 | @cl:doc(generic serialize) 3 | @cl:doc(method serialize (object integer) stream) 4 | @cl:doc(method serialize (object float) stream) 5 | @cl:doc(method serialize (object string) stream) 6 | ) 7 | -------------------------------------------------------------------------------- /docs/includes/nodes.scr: -------------------------------------------------------------------------------- 1 | @cl:with-package[name="my-app"]( 2 | @cl:doc(function my-function) 3 | @cl:doc(macro my-macro) 4 | ) 5 | -------------------------------------------------------------------------------- /docs/includes/overview.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Overview) 3 | 4 | My library does X, Y and Z. 5 | 6 | @end(section) 7 | -------------------------------------------------------------------------------- /docs/includes/spec.scr: -------------------------------------------------------------------------------- 1 | @cl:spec(call-next-method) 2 | @cl:spec(complex) 3 | -------------------------------------------------------------------------------- /docs/internals.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Internals) 3 | 4 | @begin(section) 5 | @title(Manifest) 6 | 7 | The manifest and its components are represented by the following classes: 8 | 9 | @cl:with-package[name="codex.manifest"]( 10 | @cl:doc(class manifest) 11 | @cl:doc(class document) 12 | @cl:doc(class output-format) 13 | @cl:doc(class html) 14 | @cl:doc(class single-html) 15 | @cl:doc(class multi-html) 16 | ) 17 | 18 | @end(section) 19 | 20 | @begin(section) 21 | @title(Errors) 22 | 23 | The following is a list of the error conditions Codex can signal: 24 | 25 | @cl:with-package[name="codex.error"]( 26 | @cl:doc(condition codex-error) 27 | @cl:doc(condition manifest-error) 28 | @cl:doc(condition unsupported-output-format) 29 | @cl:doc(condition template-error) 30 | ) 31 | 32 | @end(section) 33 | 34 | @end(section) 35 | -------------------------------------------------------------------------------- /docs/manifest.lisp: -------------------------------------------------------------------------------- 1 | (:docstring-markup-format :scriba 2 | :systems (:codex) 3 | :documents ((:title "Codex" 4 | :authors ("Fernando Borretti") 5 | :output-format (:type :multi-html 6 | :template :gamma) 7 | :sources ("overview.scr" 8 | "tutorial.scr" 9 | "templates.scr" 10 | "authoring-templates.scr" 11 | "internals.scr")))) 12 | -------------------------------------------------------------------------------- /docs/minima.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/docs/minima.png -------------------------------------------------------------------------------- /docs/overview.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Overview) 3 | 4 | Codex is a documentation generator. 5 | 6 | @end(section) 7 | -------------------------------------------------------------------------------- /docs/templates.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Templates) 3 | 4 | Codex ships with two templates, @b(Minima) and @b(Gamma). 5 | 6 | @begin(section) 7 | @title(Minima) 8 | 9 | @image[src="minima.png"]() 10 | 11 | Minima is, as the name implies, a minimalist theme. It's name in manifest files 12 | is @c(:minima). 13 | 14 | @end(section) 15 | 16 | @begin(section) 17 | @title(Gamma) 18 | 19 | @image[src="gamma.png"]() 20 | 21 | Gamma is the template you're looking at. It's inspired by the 22 | @link[uri="https://stripe.com/docs"](Stripe documentation). 23 | 24 | You can use it by setting @c(:template) to @c(:gamma) in manifest. 25 | 26 | @end(section) 27 | 28 | @end(section) 29 | -------------------------------------------------------------------------------- /docs/tutorial.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Tutorial) 3 | 4 | This tutorial goes through the process of documenting a Lisp project with Codex. 5 | 6 | @begin(section) 7 | @title(Getting Started) 8 | 9 | First, ceate a folder for your documents. The default is named @c(docs) in 10 | your project's root directory (the same directory as the ASDF file), but if 11 | you use another directory, an additional argument will need to be passed to 12 | @c(codex:document) at the end of this tutorial. There, you need to create two files: 13 | @c(manifest.lisp) and @c(manual.scr). 14 | 15 | The @c(manifest.lisp) file is read by Codex to figure out how to generate your 16 | docs, and in your case looks like this: 17 | 18 | @code[lang=lisp](@include[path="includes/manifest.lisp"]()) 19 | 20 | What this tells Codex is, mainly: 21 | 22 | @begin(list) 23 | 24 | @item(Use Scriba to parse the docstrings.) 25 | 26 | @item(Before generating the docs, load and extract docstrings from the 27 | @c(my-library) system.) 28 | 29 | @item(Generate a document titled "My Library" from the @c(manual.scr) file.) 30 | 31 | @item(Write the document to mutiple HTML files using the Minima template.) 32 | 33 | @end(list) 34 | 35 | The @c(manual.scr) file will be fairly bare-bones for now: 36 | 37 | @code[lang=scribe](@include[path="includes/overview.scr"]()) 38 | 39 | @end(section) 40 | 41 | @begin(section) 42 | @title(Writing with Scriba) 43 | 44 | You can find the Scriba reference 45 | @link[uri="http://commondoc.github.io/scriba/docs/reference.html"](here). 46 | 47 | @end(section) 48 | 49 | @begin(section) 50 | @title(Inserting API Documentation) 51 | 52 | To insert documentation of some Common Lisp construct @l(ndash) a function, 53 | class, macro, condition, etc. @l(ndash) you use the @c(cl:with-package) and 54 | @c(cl:doc) macros. For instance: 55 | 56 | @code[lang=scriba](@include[path="includes/nodes.scr"]()) 57 | 58 | This tells Codex to find a function named @c(my-function) and a macro named 59 | @c(my-macro) in the @c(my-app) package, and insert their API documentation. In 60 | the case of functions and macros, the only documentation that is inserted is 61 | their name, lambda list and documentation string, but other objects have more 62 | complex API documentation. For instance, classes include documentatio strings 63 | for all their slots. 64 | 65 | The @c(cl:doc) macro takes its arguments as a space-separated list of strings in 66 | the body. The first argument is the documentation type (e.g., "function" or 67 | "class"), and the second the symbol name of the thing to insert. For methods, 68 | you can add the lambda list of the method you want to insert. For example: 69 | 70 | @code[lang=scriba](@include[path="includes/methods.scr"]()) 71 | 72 | Belows is the full list of API documentation types: 73 | 74 | @begin(list) 75 | @term(@c(function)) 76 | @term(@c(setf-function)) 77 | @term(@c(macro)) 78 | @term(@c(generic)) 79 | @term(@c(setf-generic)) 80 | @term(@c(method)) 81 | @term(@c(setf-method)) 82 | @term(@c(variable)) 83 | @term(@c(struct)) 84 | @term(@c(class)) 85 | @term(@c(condition)) 86 | @term(@c(type)) 87 | @term(@c(cfunction)) 88 | @term(@c(ctype)) 89 | @term(@c(cstruct)) 90 | @term(@c(cunion)) 91 | @term(@c(cenum)) 92 | @term(@c(cbitfield)) 93 | @end(list) 94 | 95 | The documentation types prefixed with a 'c' refer to CFFI constructs, i.e., 96 | @c(cfunction) refers to a C function declared with CFFI's @c(defcfun) macro. 97 | The documentation types prefixed with a 'setf-' refer to setf versions 98 | of their suffix, i.e., @c(setf-function) refers to a setf function. 99 | 100 | @end(section) 101 | 102 | @begin(section) 103 | @title(Docstrings) 104 | 105 | Codex parses documentation strings using the format you specify in the manifest 106 | with the @c(:docstring-markup-format) option. In this case, we've chosen Scriba, 107 | which means we can insert Scriba markup in docstrings. For example: 108 | 109 | @code[lang=lisp](@include[path="includes/docstring.lisp"]()) 110 | 111 | @end(section) 112 | 113 | @begin(section) 114 | @title(Linking to the CLHS) 115 | 116 | You can use the @c(cl:spec) macro to link to a part of the Common Lisp 117 | HyperSpec. 118 | 119 | For example, the following: 120 | 121 | @code[lang=lisp](@include[path="includes/spec.scr"]()) 122 | 123 | will produce links to @cl:spec(call-next-method) and @cl:spec(complex). 124 | 125 | @end(section) 126 | 127 | @begin(section) 128 | @title(Building) 129 | 130 | Finally, run @c((codex:document :my-system)); or if you put your documents in 131 | a non-default directory or named your manifest file something other than 132 | @c(manifest.lisp), run @c((codex:document :my-system :manifest-path PATH)) 133 | where PATH is the path of the manifest file relative to the the ASDF file 134 | of @c(:my-system) (e.g. @c(#p"../project-docs/code/code-docs-manifest.lisp")). 135 | You'll see some compilation output and a lot of lines with 136 | "Inserting documentation for...". When that's done, you can open the resulting 137 | HTML in your browser. Codex signals a condition when undocumented node (in 138 | example, function, class or class slot) is encountered. Then you can choose 139 | @c(use-docstring) restart and enter a new docstring interactively. Also you 140 | may wish to silently skip all undocumented nodes. This can be achieved by 141 | specifing @c(:skip-undocumented t) key to @c(codex:document) or setting 142 | @c(codex:*skip-undocumented*) variable to T. In any case Codex will print 143 | summary on undocumented nodes encountered. 144 | 145 | @end(section) 146 | 147 | @end(section) 148 | -------------------------------------------------------------------------------- /roswell/codex.ros: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #|-*- mode:lisp -*-|# 3 | #| 4 | exec ros -Q -- $0 "$@" 5 | |# 6 | 7 | (ql:quickload :codex :silent t) 8 | 9 | (defun help () 10 | (format t "Usage: 11 | codex [OPTION | SYSTEM-NAME] 12 | 13 | Generates the documentation of the given SYSTEM-NAME using #'CODEX:DOCUMENT. 14 | 15 | Options: 16 | -h, --help 17 | Show this help.~%")) 18 | 19 | (defun main (&optional $1 &rest argv) 20 | (declare (ignore argv)) 21 | (if (or (not $1) 22 | (member $1 '("help" "-h" "-help" "--help") :test #'string=)) 23 | (help) 24 | (handler-case (codex:document $1) 25 | (error (condition) 26 | (format t "~A~%" condition))))) 27 | -------------------------------------------------------------------------------- /src/codex.lisp: -------------------------------------------------------------------------------- 1 | (defpackage codex 2 | (:use :cl) 3 | (:import-from :common-html.template 4 | :with-template) 5 | (:import-from :common-html.multi-emit 6 | :multi-emit) 7 | (:import-from :codex.markup 8 | :*current-markup-format*) 9 | (:import-from :codex.manifest 10 | :document-title 11 | :document-sources 12 | :document-output-format 13 | :output-html-template 14 | :output-html-template-options) 15 | (:export :document 16 | :quickstart 17 | :*skip-undocumented*) 18 | (:documentation "The main interface.")) 19 | (in-package :codex) 20 | 21 | (defvar *undocumented-list* nil 22 | "List of undocumented nodes") 23 | 24 | (defvar *skip-undocumented* nil 25 | "If this variable is not NIL, do not call a debugger when undocumented node 26 | is found.") 27 | 28 | (defun load-document (document directory) 29 | "Load a CommonDoc document from the sources of a Codex document." 30 | ;; Take all the document sources, concatenate them all, and parse them. 31 | (let* ((sources (loop for namestring in (document-sources document) 32 | collecting (merge-pathnames (parse-namestring namestring) 33 | directory))) 34 | ;; Set the markup format 35 | (*current-markup-format* (pandocl:guess-format (first sources))) 36 | ;; Parse the document 37 | (base-doc (codex.markup:parse-string 38 | (apply #'concatenate 39 | 'string 40 | (loop for file in sources collecting 41 | (uiop:read-file-string file)))))) 42 | (make-instance 'common-doc:document 43 | :title (document-title document) 44 | :children (common-doc:children base-doc)))) 45 | 46 | (defun copy-images (document directory) 47 | "Copy all the images from a CommonDoc document." 48 | (let ((images (common-doc.ops:collect-images document))) 49 | (loop for image in images do 50 | (handler-case 51 | (let* ((source (common-doc.file:absolute-path (common-doc:source image))) 52 | (new-file (make-pathname :name (pathname-name source) 53 | :type (pathname-type source) 54 | :defaults directory))) 55 | (uiop:copy-file source new-file)) 56 | (error () 57 | ;; External image 58 | t))))) 59 | 60 | (defun build-document (document directory) 61 | "Build a document." 62 | (let* ((doc (load-document document directory)) 63 | (build-directory (make-pathname :directory (list :relative 64 | (cl-slug:slugify (common-doc:title doc))))) 65 | (html-directory (merge-pathnames #p"html/" build-directory)) 66 | (build-directory (merge-pathnames html-directory 67 | (merge-pathnames #p"build/" 68 | directory))) 69 | (output-format (document-output-format document)) 70 | (html-template (codex.tmpl:find-template 71 | (output-html-template output-format))) 72 | ;; For document-external resources 73 | (common-doc.file:*base-directory* directory)) 74 | ;; Expand macros 75 | (let ((doc (common-doc.macro:expand-macros doc))) 76 | ;; Delete the build directory 77 | (when (probe-file build-directory) 78 | (uiop:delete-directory-tree build-directory :validate t)) 79 | ;; Ensure every section has a reference 80 | (setf doc (common-doc.ops:fill-unique-refs doc)) 81 | ;; Now we have a document, lets emit the HTML 82 | (if html-template 83 | (with-template (html-template :output-directory build-directory 84 | :options (output-html-template-options output-format)) 85 | (multi-emit doc build-directory :max-depth 1) 86 | (copy-images doc build-directory)) 87 | (error 'codex.error:template-error 88 | :template-name html-template 89 | :message "No such template known.")) 90 | doc))) 91 | 92 | (defun undocumented-handler (c) 93 | "Handle undocumented nodes" 94 | (push (codex.error:node c) 95 | *undocumented-list*) 96 | (if (and *skip-undocumented* 97 | (find-restart 'codex.macro:use-docstring)) 98 | (invoke-restart 'codex.macro:use-docstring ""))) 99 | 100 | (defun print-undocumented (undocumented) 101 | (flet ((print-node (node) 102 | (format t "No docstring for ~a (of type ~a)~%" 103 | (docparser:node-name node) 104 | (type-of node)))) 105 | (mapc #'print-node undocumented))) 106 | 107 | (defun build-manifest (manifest directory) 108 | "Build a manifest. Return a list of nodes which do not have a docstring." 109 | ;; First, load all the systems, extracting documentation information into the 110 | ;; global index 111 | (let ((codex.macro:*index* (docparser:parse (codex.manifest:manifest-systems manifest))) 112 | (*current-markup-format* (codex.manifest:manifest-markup-format manifest)) 113 | *undocumented-list*) 114 | ;; Go through each document, building it 115 | (loop for document in (codex.manifest:manifest-documents manifest) do 116 | (handler-bind 117 | ((codex.error:no-docstring #'undocumented-handler)) 118 | (build-document document directory))) 119 | *undocumented-list*)) 120 | 121 | (defun document (system-name &key (skip-undocumented *skip-undocumented*) 122 | manifest-path) 123 | "Generate documentation for a system. @c(skip-undocumented) overrides @c(*skip-undocumented*) 124 | @c(manifest-path) overrides @c(*default-manifest-pathname*) which is 125 | @c(#p\"docs/manifest.lisp\")" 126 | (check-type manifest-path (or null pathname)) 127 | (let ((manifest-pathname (codex.manifest:system-manifest-pathname system-name 128 | :manifest-path manifest-path))) 129 | (unless (probe-file manifest-pathname) 130 | (error 'codex.error:manifest-error 131 | :system-name system-name 132 | :message "No manifest.")) 133 | (let ((manifest (codex.manifest:parse-manifest manifest-pathname)) 134 | (directory (uiop:pathname-directory-pathname manifest-pathname)) 135 | (*skip-undocumented* skip-undocumented)) 136 | (print-undocumented 137 | (build-manifest manifest directory)))) 138 | nil) 139 | -------------------------------------------------------------------------------- /src/error.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage codex.error 3 | (:use :cl) 4 | ;; Classes 5 | (:export :codex-error 6 | :manifest-error 7 | :unsupported-output-format 8 | :template-error 9 | :no-docstring) 10 | ;; Accessors 11 | (:export :system-name 12 | :message 13 | :format-name 14 | :template-name 15 | :node) 16 | (:documentation "Codex errors.")) 17 | (in-package :codex.error) 18 | 19 | (define-condition codex-error () 20 | () 21 | (:documentation "The base class of all Codex errors.")) 22 | 23 | (define-condition manifest-error (codex-error) 24 | ((system-name :reader system-name 25 | :initarg :system-name 26 | :type keyword 27 | :documentation "The name of the system.") 28 | (message :reader message 29 | :initarg :message 30 | :type string 31 | :documentation "The error message.")) 32 | (:report 33 | (lambda (condition stream) 34 | (format stream "Manifest error for system ~A: ~A" 35 | (system-name condition) 36 | (message condition)))) 37 | (:documentation "Signalled when there is an error parsing a manifest file.")) 38 | 39 | (define-condition unsupported-output-format (codex-error) 40 | ((format-name :reader format-name 41 | :initarg :format-name 42 | :type keyword 43 | :documentation "The name of the output format.")) 44 | (:report 45 | (lambda (condition stream) 46 | (format stream "Unsupported output format: ~A." (format-name condition)))) 47 | (:documentation "Signalled when Codex doesn't know the output format a 48 | document specifies.")) 49 | 50 | (define-condition template-error (codex-error) 51 | ((template-name :reader template-name 52 | :initarg :template-name 53 | :type keyword 54 | :documentation "The template name.") 55 | (message :reader message 56 | :initarg :message 57 | :type string 58 | :documentation "The error message.")) 59 | (:report 60 | (lambda (condition stream) 61 | (format stream "Error with template ~A: ~A." 62 | (template-name condition) 63 | (message condition)))) 64 | (:documentation "Signalled by errors related to templates.")) 65 | 66 | 67 | (define-condition no-docstring (codex-error) 68 | ((node :reader node 69 | :initarg :node 70 | :documentation "The node without docstring.")) 71 | (:report 72 | (lambda (c s) 73 | (let ((node (node c))) 74 | (format s "No docstring in node ~a(~a)" 75 | node (docparser:node-name node))))) 76 | (:documentation "Signalled when a node has no docstring.")) 77 | -------------------------------------------------------------------------------- /src/macro.lisp: -------------------------------------------------------------------------------- 1 | (defpackage codex.macro 2 | (:use :cl) 3 | (:import-from :common-doc 4 | :content-node 5 | :text-node 6 | :code 7 | :list-item 8 | :unordered-list 9 | ;; Operators 10 | :define-node 11 | :children 12 | :text 13 | ;; Functions 14 | :make-text 15 | :make-meta 16 | :make-web-link 17 | :make-row 18 | :make-cell) 19 | (:import-from :common-doc.macro 20 | :macro-node 21 | :expand-macro) 22 | (:export :*index* 23 | :cl-doc 24 | :with-package 25 | :param 26 | :spec 27 | :use-docstring) 28 | (:documentation "CommonDoc macros for Codex.")) 29 | (in-package :codex.macro) 30 | 31 | ;;; Variables 32 | 33 | (defvar *index* nil 34 | "The current Docparser index.") 35 | 36 | (defvar *current-package-name* nil 37 | "The name of the current package. Set by the with-package macro.") 38 | 39 | ;;; Macro classes 40 | 41 | (define-node cl-doc (macro-node) 42 | () 43 | (:tag-name "cl:doc") 44 | (:documentation "Insert documentation of a node.")) 45 | 46 | (define-node with-package (macro-node) 47 | ((name :reader package-macro-name 48 | :type string 49 | :attribute-name "name" 50 | :documentation "The package's name.")) 51 | (:tag-name "cl:with-package") 52 | (:documentation "Set the current package to use in the body.")) 53 | 54 | (define-node param (macro-node) 55 | () 56 | (:tag-name "cl:param") 57 | (:documentation "An argument of an operator.")) 58 | 59 | (define-node spec (macro-node) 60 | () 61 | (:tag-name "cl:spec") 62 | (:documentation "Add a link to the Common Lisp HyperSpec.")) 63 | 64 | ;;; Utilities 65 | 66 | (defun slot-exists-and-bound-p (object slot-name) 67 | (and (slot-exists-p object slot-name) 68 | (slot-boundp object slot-name))) 69 | 70 | (defun make-class-metadata (class) 71 | "Create metadata for HTML classes." 72 | (make-meta 73 | (list 74 | (cons "html:class" (if (listp class) 75 | (format nil "~{codex-~A~#[~:; ~]~}" class) 76 | (concatenate 'string 77 | "codex-" 78 | class)))))) 79 | 80 | (defmethod name-node (node) 81 | "Create a node representing the name of a node. If it is a setf operation, 82 | \"setf \" is automatically prepended before the name text." 83 | (make-instance 'code 84 | :metadata (make-class-metadata "name") 85 | :children (list 86 | (make-text 87 | (concatenate 'string 88 | (if (and (typep node 'docparser:operator-node) 89 | (docparser:operator-setf-p node)) 90 | "setf " "") 91 | (docparser:render-humanize 92 | (docparser:node-name node))))))) 93 | 94 | (defun check-node-docstring (node) 95 | "Check and return the node's docstring. 96 | If there is none, codex.error:no-docstring condition is signalled." 97 | (let ((docstring (docparser:node-docstring node))) 98 | (if docstring docstring 99 | (error 'codex.error:no-docstring :node node)))) 100 | 101 | (defun docstring-node (node) 102 | "Create a node representing a node's docstring." 103 | (make-instance 'content-node 104 | :metadata (make-class-metadata "docstring") 105 | :children 106 | (restart-case 107 | (children 108 | (codex.markup:parse-string 109 | (check-node-docstring node))) 110 | (use-docstring (docstring) 111 | :report "Enter a new docstring" 112 | :interactive (lambda () 113 | (format *query-io* "Enter a new docstring: ") 114 | (force-output *query-io*) 115 | (list (read-line *query-io*))) 116 | (list (make-text docstring)))))) 117 | 118 | (defun list-to-code-node (class list) 119 | (make-instance 'code 120 | :metadata (make-class-metadata class) 121 | :children (list 122 | (make-text 123 | (with-standard-io-syntax 124 | (let ((*print-case* :downcase)) 125 | (princ-to-string list))))))) 126 | 127 | (defun write-to-code-node (class obj) 128 | (make-instance 'code 129 | :metadata (make-class-metadata class) 130 | :children (list 131 | (make-text 132 | (with-standard-io-syntax 133 | (let ((*print-case* :downcase)) 134 | (write-to-string obj))))))) 135 | 136 | (defun make-doc-node (classes &rest children) 137 | (make-instance 'content-node 138 | :metadata (make-class-metadata (append (list "doc-node") 139 | classes)) 140 | :children children)) 141 | 142 | ;;; Docparser nodes to CommonDoc nodes 143 | 144 | (defgeneric expand-node (node) 145 | (:documentation "Turn a Docparser node into a CommonDoc one.")) 146 | 147 | (defun expand-operator-node (node class-name) 148 | "Expand a generic operator node. Called by more specific methods." 149 | (make-doc-node (list "operator" class-name) 150 | (name-node node) 151 | (list-to-code-node "lambda-list" 152 | (docparser:operator-lambda-list node)) 153 | (docstring-node node))) 154 | 155 | (defmethod expand-node ((node docparser:function-node)) 156 | "Expand a function node." 157 | (expand-operator-node node "function")) 158 | 159 | (defmethod expand-node ((node docparser:macro-node)) 160 | "Expand a macro node." 161 | (expand-operator-node node "macro")) 162 | 163 | (defmethod expand-node ((node docparser:generic-function-node)) 164 | "Expand a generic function node." 165 | (expand-operator-node node "generic-function")) 166 | 167 | (defmethod expand-node ((node docparser:method-node)) 168 | "Expand a method node." 169 | (expand-operator-node node "method")) 170 | 171 | (defmethod expand-node ((node docparser:operator-node)) 172 | "Backup method when someone has created a subclass of operator-node that's not 173 | explicitly supported by this method." 174 | (expand-operator-node node "operator")) 175 | 176 | (defmethod expand-node ((node docparser:struct-slot-node)) 177 | "Expand a structure slot node. This doesn't have any docstrings." 178 | ;; The slot options are going to be in a table of option-value pairs on each 179 | ;; row if the version of docparser is new enough to be able to get them. 180 | ;; Otherwise no table will be actually added to the document. The first row 181 | ;; is a header. 182 | ;; 183 | ;; The options will be in the following order: 184 | ;; 185 | ;; 1. type 186 | ;; 2. readonly 187 | ;; 3. accessor, if available 188 | ;; 4. initform, if available 189 | (let* ((left-col-metadata (make-class-metadata "class-struct-slot-option-label-cell")) 190 | (header-metadata (make-class-metadata "class-struct-slot-option-header-cell")) 191 | (symbol-list-metadata (make-class-metadata '("class-struct-slot-option-value-cell" 192 | "class-struct-slot-option-symbol-list-cell"))) 193 | (row-metadata (make-class-metadata "class-struct-slot-option-row")) 194 | (rows (append 195 | (list (make-row (list (make-cell (list (make-text "Option")) 196 | :metadata header-metadata) 197 | (make-cell (list (make-text "Value")) 198 | :metadata header-metadata)) 199 | :metadata row-metadata)) 200 | (list (make-row (list (make-cell (list (make-text "Type:")) 201 | :metadata left-col-metadata) 202 | (make-cell (list (write-to-code-node 203 | "class-struct-slot-symbol-list" 204 | (docparser::struct-slot-type node))) 205 | :metadata symbol-list-metadata)) 206 | :metadata row-metadata)) 207 | (list (make-row (list (make-cell (list (make-text "Read Only:")) 208 | :metadata left-col-metadata) 209 | (make-cell (list (write-to-code-node 210 | "class-struct-slot-symbol-list" 211 | (docparser::struct-slot-read-only node))) 212 | :metadata symbol-list-metadata)) 213 | :metadata row-metadata)) 214 | ;; Only include accessor and initform if they are available. 215 | (when (docparser::struct-slot-accessor node) 216 | (list (make-row (list (make-cell (list (make-text "Accessor:")) 217 | :metadata left-col-metadata) 218 | (make-cell (list (write-to-code-node 219 | "class-struct-slot-symbol-list" 220 | (docparser::struct-slot-accessor node))) 221 | :metadata symbol-list-metadata)) 222 | :metadata row-metadata))) 223 | (multiple-value-bind (initform initform-exists) 224 | (docparser:slot-initform node) 225 | (when initform-exists 226 | (list (make-row (list (make-cell (list (make-text "Initform:")) 227 | :metadata left-col-metadata) 228 | (make-cell (list (write-to-code-node 229 | "class-struct-slot-symbol-list" 230 | initform)) 231 | :metadata symbol-list-metadata)) 232 | :metadata row-metadata))))))) 233 | (make-instance 'list-item 234 | :metadata (make-class-metadata (list "slot" "structure-slot")) 235 | :children 236 | (append 237 | (list (name-node node)) 238 | (when (< 1 (list-length rows)) 239 | (list (make-instance 'content-node 240 | :metadata 241 | (make-class-metadata "class-struct-slot-option-node") 242 | :children 243 | (list 244 | (common-doc:make-table 245 | rows 246 | :metadata 247 | (make-class-metadata 248 | "class-struct-slot-option-table")))))))))) 249 | 250 | (defmethod expand-node ((node docparser:class-slot-node)) 251 | "Expand a class slot node." 252 | ;; The slot options are going to be in a table of option-value pairs on each 253 | ;; row. The first row is a header. 254 | ;; 255 | ;; The options will be in the following order: 256 | ;; 257 | ;; 1. allocation 258 | ;; 2. type 259 | ;; 3. initarg, if available 260 | ;; 4. initform, if available 261 | ;; 5. readers, if available 262 | ;; 6. writers, if available 263 | ;; 7. accessors, if available 264 | (let* ((left-col-metadata (make-class-metadata "class-struct-slot-option-label-cell")) 265 | (header-metadata (make-class-metadata "class-struct-slot-option-header-cell")) 266 | (symbol-list-metadata (make-class-metadata '("class-struct-slot-option-value-cell" 267 | "class-struct-slot-option-symbol-list-cell"))) 268 | (row-metadata (make-class-metadata "class-struct-slot-option-row")) 269 | (rows (append 270 | (list (make-row (list (make-cell (list (make-text "Option")) 271 | :metadata header-metadata) 272 | (make-cell (list (make-text "Value")) 273 | :metadata header-metadata)) 274 | :metadata row-metadata)) 275 | (list (make-row (list (make-cell (list (make-text "Allocation:")) 276 | :metadata left-col-metadata) 277 | (make-cell (list (make-text 278 | (docparser:render-humanize 279 | (docparser:slot-allocation node)))) 280 | :metadata symbol-list-metadata)) 281 | :metadata row-metadata)) 282 | (list (make-row (list (make-cell (list (make-text "Type:")) 283 | :metadata left-col-metadata) 284 | (make-cell (list (write-to-code-node 285 | "class-struct-slot-symbol-list" 286 | (docparser:slot-type node))) 287 | :metadata symbol-list-metadata)) 288 | :metadata row-metadata)) 289 | ;; Only include initarg, initform, readers, writers, and 290 | ;; accessors if they are available. 291 | (when (docparser:slot-initarg node) 292 | (list (make-row (list (make-cell (list (make-text "Initarg:")) 293 | :metadata left-col-metadata) 294 | (make-cell (list (write-to-code-node 295 | "class-struct-slot-symbol-list" 296 | (docparser:slot-initarg node))) 297 | :metadata symbol-list-metadata)) 298 | :metadata row-metadata))) 299 | (when (second (multiple-value-list (docparser:slot-initform node))) 300 | (list (make-row (list (make-cell (list (make-text "Initform:")) 301 | :metadata left-col-metadata) 302 | (make-cell (list (write-to-code-node 303 | "class-struct-slot-symbol-list" 304 | (docparser:slot-initform node))) 305 | :metadata symbol-list-metadata)) 306 | :metadata row-metadata))) 307 | (when (docparser:slot-readers node) 308 | (list (make-row (list (make-cell (list (make-text "Readers:")) 309 | :metadata left-col-metadata) 310 | (make-cell (list (list-to-code-node 311 | "class-struct-slot-symbol-list" 312 | (docparser:slot-readers node))) 313 | :metadata symbol-list-metadata)) 314 | :metadata row-metadata))) 315 | (when (docparser:slot-writers node) 316 | (list (make-row (list (make-cell (list (make-text "Writers:")) 317 | :metadata left-col-metadata) 318 | (make-cell (list (list-to-code-node 319 | "class-struct-slot-symbol-list" 320 | (docparser:slot-writers node))) 321 | :metadata symbol-list-metadata)) 322 | :metadata row-metadata))) 323 | (when (docparser:slot-accessors node) 324 | (list (make-row (list (make-cell (list (make-text "Accessors:")) 325 | :metadata left-col-metadata) 326 | (make-cell (list (list-to-code-node 327 | "class-struct-slot-symbol-list" 328 | (docparser:slot-accessors node))) 329 | :metadata symbol-list-metadata)) 330 | :metadata row-metadata)))))) 331 | (make-instance 'list-item 332 | :metadata (make-class-metadata (list "slot" "class-slot")) 333 | :children 334 | (list (name-node node) 335 | (docstring-node node) 336 | (make-instance 'content-node 337 | :metadata 338 | (make-class-metadata "class-struct-slot-option-node") 339 | :children 340 | (list 341 | (common-doc:make-table 342 | rows 343 | :metadata 344 | (make-class-metadata 345 | "class-struct-slot-option-table")))))))) 346 | 347 | (defun expand-record-node (class node) 348 | ;; The middle holds the class/struct attributes. Some entries can only be 349 | ;; gotten in newer versions of docparser, so they are only gotten if 350 | ;; possible. 351 | (let ((beginning (list (list "record" class) 352 | (name-node node))) 353 | (middle (let* ((left-col-metadata (make-class-metadata 354 | "class-struct-slot-option-label-cell")) 355 | (header-metadata (make-class-metadata 356 | "class-struct-slot-option-header-cell")) 357 | (symbol-list-metadata (make-class-metadata 358 | '("class-struct-slot-option-value-cell" 359 | "class-struct-slot-option-symbol-list-cell"))) 360 | (row-metadata (make-class-metadata "class-struct-slot-option-row")) 361 | (header-row (list (make-row (list (make-cell (list (make-text "Option")) 362 | :metadata header-metadata) 363 | (make-cell (list (make-text "Value")) 364 | :metadata header-metadata)) 365 | :metadata row-metadata))) 366 | (rows (if (typep node 'docparser:class-node) 367 | ;; For classes, they are, in order 368 | ;; 369 | ;; 1. superclasses 370 | ;; 2. metaclass, if available 371 | ;; 3. default-initargs, if possible 372 | (append 373 | (list (make-row 374 | (list (make-cell (list (make-text "Superclasses:")) 375 | :metadata left-col-metadata) 376 | (make-cell 377 | (list 378 | (list-to-code-node 379 | "class-struct-slot-symbol-list" 380 | (append (docparser:class-node-superclasses node) 381 | (list t)))) 382 | :metadata symbol-list-metadata)) 383 | :metadata row-metadata)) 384 | (when (slot-exists-and-bound-p node 'docparser::metaclass) 385 | (list (make-row 386 | (list (make-cell (list (make-text "Metaclass:")) 387 | :metadata left-col-metadata) 388 | (make-cell 389 | (list (write-to-code-node 390 | "class-struct-slot-symbol-list" 391 | (docparser::class-node-metaclass node))) 392 | :metadata symbol-list-metadata)) 393 | :metadata row-metadata))) 394 | (when (slot-exists-and-bound-p node 'docparser::default-initargs) 395 | (list (make-row 396 | (list (make-cell 397 | (list (make-text "Default Initargs:")) 398 | :metadata left-col-metadata) 399 | (make-cell 400 | (list (write-to-code-node 401 | "class-struct-slot-symbol-list" 402 | (docparser::class-node-default-initargs 403 | node))) 404 | :metadata symbol-list-metadata)) 405 | :metadata row-metadata)))) 406 | ;; For structs, they are, in order (note none 407 | ;; of them are available for older versions of 408 | ;; docparser) 409 | ;; 410 | ;; 1. constructor 411 | ;; 2. predicate 412 | ;; 3. copier 413 | ;; 4. print-function, if available 414 | ;; 5. print-object, if available 415 | ;; 6. type, if something other than nil 416 | ;; 7. named, if meaningful 417 | ;; 8. initial-offset, if meaningful 418 | (when (every (lambda (slot) (slot-exists-and-bound-p node slot)) 419 | '(docparser::constructor 420 | docparser::predicate 421 | docparser::copier 422 | docparser::print-function 423 | docparser::print-object 424 | docparser::type 425 | docparser::named 426 | docparser::initial-offset)) 427 | (append 428 | (list (make-row 429 | (list (make-cell (list (make-text "Constructor:")) 430 | :metadata left-col-metadata) 431 | (make-cell 432 | (list 433 | (list-to-code-node 434 | "class-struct-slot-symbol-list" 435 | (docparser::struct-node-constructor node))) 436 | :metadata symbol-list-metadata)) 437 | :metadata row-metadata)) 438 | (list (make-row 439 | (list (make-cell (list (make-text "Predicate:")) 440 | :metadata left-col-metadata) 441 | (make-cell 442 | (list 443 | (list-to-code-node 444 | "class-struct-slot-symbol-list" 445 | (docparser::struct-node-predicate node))) 446 | :metadata symbol-list-metadata)) 447 | :metadata row-metadata)) 448 | (list (make-row 449 | (list (make-cell (list (make-text "Copier:")) 450 | :metadata left-col-metadata) 451 | (make-cell 452 | (list 453 | (list-to-code-node 454 | "class-struct-slot-symbol-list" 455 | (docparser::struct-node-copier node))) 456 | :metadata symbol-list-metadata)) 457 | :metadata row-metadata)) 458 | (when (docparser::struct-node-print-function node) 459 | (list (make-row 460 | (list (make-cell (list (make-text "Print Function:")) 461 | :metadata left-col-metadata) 462 | (make-cell 463 | (list 464 | (list-to-code-node 465 | "class-struct-slot-symbol-list" 466 | (docparser::struct-node-print-function 467 | node))) 468 | :metadata symbol-list-metadata)) 469 | :metadata row-metadata))) 470 | (when (docparser::struct-node-print-object node) 471 | (list (make-row 472 | (list (make-cell (list (make-text "Print Object:")) 473 | :metadata left-col-metadata) 474 | (make-cell 475 | (list 476 | (list-to-code-node 477 | "class-struct-slot-symbol-list" 478 | (docparser::struct-node-print-object node))) 479 | :metadata symbol-list-metadata)) 480 | :metadata row-metadata))) 481 | (when (docparser::struct-node-type node) 482 | (list (make-row 483 | (list (make-cell (list (make-text "Type:")) 484 | :metadata left-col-metadata) 485 | (make-cell 486 | (list 487 | (write-to-code-node 488 | "class-struct-slot-symbol-list" 489 | (docparser::struct-node-type node))) 490 | :metadata symbol-list-metadata)) 491 | :metadata row-metadata) 492 | (make-row 493 | (list (make-cell (list (make-text "Named:")) 494 | :metadata left-col-metadata) 495 | (make-cell 496 | (list 497 | (list-to-code-node 498 | "class-struct-slot-symbol-list" 499 | (docparser::struct-node-named node))) 500 | :metadata symbol-list-metadata)) 501 | :metadata row-metadata) 502 | (make-row 503 | (list (make-cell (list (make-text "Initial Offset:")) 504 | :metadata left-col-metadata) 505 | (make-cell 506 | (list 507 | (list-to-code-node 508 | "class-struct-slot-symbol-list" 509 | (docparser::struct-node-initial-offset 510 | node))) 511 | :metadata symbol-list-metadata)) 512 | :metadata row-metadata)))))))) 513 | (when rows 514 | (list (make-instance 'content-node 515 | :metadata 516 | (make-class-metadata "class-struct-slot-option-node") 517 | :children 518 | (list 519 | (common-doc:make-table 520 | (cons header-row rows) 521 | :metadata 522 | (make-class-metadata 523 | "class-struct-slot-option-table")))))))) 524 | (end (list (docstring-node node) 525 | (make-instance 'unordered-list 526 | :metadata (make-class-metadata "slot-list") 527 | :children 528 | (loop for slot in (docparser:record-slots node) 529 | collecting (expand-node slot)))))) 530 | (apply #'make-doc-node (append beginning middle end)))) 531 | 532 | (defmethod expand-node ((node docparser:struct-node)) 533 | "Expand a structure definition node." 534 | (expand-record-node "structure" node)) 535 | 536 | (defmethod expand-node ((node docparser:class-node)) 537 | "Expand a class definition node." 538 | (expand-record-node "class" node)) 539 | 540 | (defmethod expand-node ((node docparser:condition-node)) 541 | "Expand a condition definition node." 542 | (expand-record-node "condition" node)) 543 | 544 | (defmethod expand-node ((node docparser:variable-node)) 545 | "Expand a variable node." 546 | (make-doc-node (list "variable") 547 | (name-node node) 548 | (docstring-node node))) 549 | 550 | (defmethod expand-node ((node docparser:documentation-node)) 551 | "Backup method when someone has created a subclass of documentation-node that's not 552 | explicitly supported by this." 553 | (make-doc-node (list) 554 | (name-node node) 555 | (docstring-node node))) 556 | 557 | (defmethod expand-node ((node docparser:type-node)) 558 | "Expand a type node." 559 | (make-doc-node (list "type") 560 | (name-node node) 561 | (list-to-code-node "type-def" 562 | (docparser:operator-lambda-list node)) 563 | (docstring-node node))) 564 | 565 | (defmethod expand-node ((node t)) 566 | "When expanding an unsupported node, rather than generate an error, simply 567 | create an error message." 568 | (make-text (format nil "Unsupported node type ~A." (type-of node)) 569 | :metadata (make-class-metadata (list "error" "unsupported-node-error")))) 570 | 571 | ;;; Macroexpansions 572 | 573 | (defparameter +type-name-to-class-map+ 574 | (list (cons "function" 'docparser:function-node) 575 | (cons "setf-function" 'docparser:function-node) 576 | (cons "macro" 'docparser:macro-node) 577 | (cons "generic" 'docparser:generic-function-node) 578 | (cons "setf-generic" 'docparser:generic-function-node) 579 | (cons "method" 'docparser:method-node) 580 | (cons "setf-method" 'docparser:method-node) 581 | (cons "variable" 'docparser:variable-node) 582 | (cons "struct" 'docparser:struct-node) 583 | (cons "class" 'docparser:class-node) 584 | (cons "condition" 'docparser:condition-node) 585 | (cons "type" 'docparser:type-node) 586 | (cons "cfunction" 'docparser:cffi-function) 587 | (cons "ctype" 'docparser:cffi-type) 588 | (cons "cstruct" 'docparser:cffi-struct) 589 | (cons "cunion" 'docparser:cffi-union) 590 | (cons "cenum" 'docparser:cffi-enum) 591 | (cons "cbitfield" 'docparser:cffi-bitfield)) 592 | "Associate the string names of Docparser classes to the corresponding 593 | docparser class names.") 594 | 595 | (defun find-node-type-by-name (name) 596 | (rest (assoc name +type-name-to-class-map+ :test #'equal))) 597 | 598 | (defun node-not-found (symbol) 599 | (make-instance 'content-node 600 | :metadata (make-class-metadata (list "error" "no-node")) 601 | :children 602 | (list 603 | (make-text "No node with name ") 604 | (make-instance 'code 605 | :children 606 | (list 607 | (make-text (string-downcase symbol)))) 608 | (make-text ".")))) 609 | 610 | (defun no-such-type (name) 611 | (make-instance 'content-node 612 | :metadata (make-class-metadata (list "error" "no-type")) 613 | :children 614 | (list 615 | (make-text "No node type with name ") 616 | (make-instance 'code 617 | :children 618 | (list (make-text name))) 619 | (make-text ".")))) 620 | 621 | (defun no-method-lambda-list (name) 622 | (make-instance 'content-node 623 | :metadata (make-class-metadata (list "error" "no-method-lambda-list")) 624 | :children 625 | (list 626 | (make-text "Need a lambda list to find the method ") 627 | (make-instance 'code 628 | :children 629 | (list (make-text (string-downcase name)))) 630 | (make-text ".")))) 631 | 632 | (defun no-method-found (name lambda-list) 633 | (make-instance 'content-node 634 | :metadata (make-class-metadata (list "error" "no-method-found")) 635 | :children 636 | (list 637 | (make-text "No method ") 638 | (make-instance 'code 639 | :children 640 | (list (make-text (string-downcase name)))) 641 | (make-text " with the lambda list ") 642 | (make-instance 'code 643 | :children 644 | (list (make-text (string-downcase lambda-list)))) 645 | (make-text " found.")))) 646 | 647 | (defun find-node (type symbol rest) 648 | (let ((class (find-node-type-by-name type)) 649 | (is-setf (alexandria:starts-with-subseq "setf-" type))) 650 | (if class 651 | ;; Use docparser to find suitable nodes and then filter out ones whose 652 | ;; are operator-nodes but their setfp slot doesn't match type (is-setf). 653 | (let ((nodes (remove-if #'(lambda (nd) 654 | (and (typep nd 'docparser:operator-node) 655 | (not (equal is-setf (docparser:operator-setf-p nd))))) 656 | (docparser:query *index* 657 | :package-name *current-package-name* 658 | :symbol-name (string-upcase symbol) 659 | :class class)))) 660 | (if (> (length nodes) 0) 661 | (if (eq class 'docparser:method-node) 662 | ;; Search for the proper method using the arglist 663 | (if (equal rest (list "")) 664 | (no-method-lambda-list symbol) 665 | (let* ((lambda-list (princ-to-string 666 | (read-from-string 667 | (format nil "(~{~A~^ ~})" rest)))) 668 | (method (find-if #'(lambda (method) 669 | (string= lambda-list 670 | (princ-to-string 671 | (docparser:operator-lambda-list 672 | method)))) 673 | nodes))) 674 | (if method 675 | (expand-node method) 676 | (no-method-found symbol lambda-list)))) 677 | ;; Not a method. 678 | (expand-node (elt nodes 0))) 679 | ;; No node with that name, report an error 680 | (node-not-found symbol))) 681 | (no-such-type type)))) 682 | 683 | (defmethod expand-macro ((node cl-doc)) 684 | (let* ((text (common-doc.ops:collect-all-text node)) 685 | (arguments (split-sequence:split-sequence #\Space text))) 686 | (destructuring-bind (type symbol &rest rest) 687 | arguments 688 | (format t "Inserting documentation for ~A ~S.~%" type symbol) 689 | (find-node type symbol rest)))) 690 | 691 | (defmethod expand-macro ((node with-package)) 692 | (let* ((package-name (package-macro-name node)) 693 | (*current-package-name* (string-upcase package-name)) 694 | (new-node (make-instance 'content-node 695 | :children (children node)))) 696 | ;; Expand inner macros 697 | (common-doc.macro:expand-macros new-node) 698 | new-node)) 699 | 700 | (defmethod expand-macro ((node param)) 701 | (make-instance 'code 702 | :metadata (make-class-metadata "param") 703 | :children (children node))) 704 | 705 | (defun url-for-symbol (symbol-name) 706 | "Return the Hyperspec or l1sp.org URL for a symbol in the CL package." 707 | (concatenate 'string "http://l1sp.org/cl/" symbol-name)) 708 | 709 | (defmethod expand-macro ((node spec)) 710 | (make-web-link (url-for-symbol (text (first (children node)))) 711 | (list 712 | (make-instance 'code 713 | :children (children node))))) 714 | -------------------------------------------------------------------------------- /src/manifest.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage codex.manifest 3 | (:use :cl) 4 | (:import-from :trivial-types 5 | :proper-list 6 | :property-list) 7 | ;; Classes 8 | (:export :output-format 9 | :html 10 | :single-html 11 | :multi-html 12 | :document 13 | :manifest) 14 | ;; Accessors 15 | (:export :output-html-template 16 | :output-html-template-options 17 | :document-title 18 | :document-authors 19 | :document-output-format 20 | :document-sources 21 | :docstring-markup-format 22 | :manifest-markup-format 23 | :manifest-systems 24 | :manifest-documents) 25 | (:export :*default-manifest-pathname* 26 | :parse-manifest 27 | :system-manifest-pathname) 28 | (:documentation "Parsing Codex manifest files.")) 29 | (defpackage :codex-manifest-user 30 | (:use :cl :codex.manifest) 31 | (:documentation "The package in which Codex manifests are read.")) 32 | (in-package :codex.manifest) 33 | 34 | (defclass output-format () 35 | () 36 | (:documentation "The base class of all output formats.")) 37 | 38 | (defclass html (output-format) 39 | ((html-template :reader output-html-template 40 | :initarg :template-name 41 | :type keyword 42 | :documentation "The name of the HTML template.") 43 | (template-options :reader output-html-template-options 44 | :initarg :template-options 45 | :initform nil 46 | :type property-list 47 | :documentation "A property list of template initargs.")) 48 | (:documentation "The base class of HTML formats.")) 49 | 50 | (defclass single-html (html) 51 | () 52 | (:documentation "Single-file HTML output.")) 53 | 54 | (defclass multi-html (html) 55 | () 56 | (:documentation "Multi-file HTML output.")) 57 | 58 | (defclass document () 59 | ((document-title :reader document-title 60 | :initarg :title 61 | :type string 62 | :documentation "The document's title.") 63 | (document-authors :reader document-authors 64 | :initarg :authors 65 | :type (proper-list string) 66 | :documentation "A list of the document's authors.") 67 | (output-format :reader document-output-format 68 | :initarg :output-format 69 | :type output-format 70 | :documentation "The document's output format.") 71 | (document-sources :reader document-sources 72 | :initarg :sources 73 | :type (proper-list pathname) 74 | :documentation "A list of pathnames to source files to 75 | build the document from.")) 76 | (:documentation "A Codex document. Project manifests can define multiple 77 | documents, e.g. a manual, a tutorial, an advanced manual.")) 78 | 79 | (defclass manifest () 80 | ((markup-format :reader manifest-markup-format 81 | :initarg :markup-format 82 | :type keyword 83 | :documentation "The markup format used in docstrings.") 84 | (systems :reader manifest-systems 85 | :initarg :systems 86 | :type (proper-list keyword) 87 | :documentation "A list of systems to document.") 88 | (documents :reader manifest-documents 89 | :initarg :documents 90 | :type (proper-list document) 91 | :documentation "A list of documents.")) 92 | (:documentation "Manifest options.")) 93 | 94 | (defun read-manifest (pathname) 95 | "Read a manifest file into an S-expression using the :codex-manifest-user 96 | package." 97 | (uiop:with-safe-io-syntax (:package (find-package :codex-manifest-user)) 98 | (uiop:read-file-form pathname))) 99 | 100 | (defun parse-output-format (plist) 101 | "Create an instance of an output-format class from a plist." 102 | (let* ((format-name (getf plist :type)) 103 | (args (alexandria:remove-from-plist plist :type)) 104 | (class-name (cond 105 | ((eq format-name :single-html) 106 | 'single-html) 107 | ((eq format-name :multi-html) 108 | 'multi-html) 109 | (t 110 | (error 'codex.error:unsupported-output-format 111 | :format-name format-name))))) 112 | (make-instance class-name 113 | :template-name (getf plist :template) 114 | :template-options (alexandria:remove-from-plist plist :template)))) 115 | 116 | (defun parse-document (document-plist) 117 | "Parse a manifest's document plist into a document object." 118 | (destructuring-bind (&key title authors output-format sources) 119 | document-plist 120 | (make-instance 'document 121 | :title title 122 | :authors authors 123 | :output-format (parse-output-format output-format) 124 | :sources sources))) 125 | 126 | (defun parse-manifest (pathname) 127 | "Parse a manifest from a pathname." 128 | (let ((plist (read-manifest pathname))) 129 | (destructuring-bind (&key docstring-markup-format systems documents) 130 | plist 131 | (make-instance 'manifest 132 | :markup-format docstring-markup-format 133 | :systems systems 134 | :documents (loop for doc in documents collecting 135 | (parse-document doc)))))) 136 | 137 | ;;; Main interface 138 | 139 | (defparameter *default-manifest-pathname* 140 | #p"docs/manifest.lisp" 141 | "The pathname of the Codex manifest in a system.") 142 | 143 | (defun system-manifest-pathname (system-name &key manifest-path) 144 | "Return the absolute pathname to a system's Codex manifest. @c(manifest-path) 145 | overrides @c(*default-manifest-pathname*) which is @c(#p\"docs/manifest.lisp\")" 146 | (asdf:system-relative-pathname system-name 147 | (if manifest-path manifest-path *default-manifest-pathname*))) 148 | -------------------------------------------------------------------------------- /src/markup.lisp: -------------------------------------------------------------------------------- 1 | (defpackage codex.markup 2 | (:use :cl) 3 | (:export :*current-markup-format* 4 | :parse-string) 5 | (:documentation "Parsing files and docstrings.")) 6 | (in-package :codex.markup) 7 | 8 | (defvar *current-markup-format* nil 9 | "The name of the markup format that will be used to parse docstrings and 10 | files. This is a keyword that is passed to Pandocl.") 11 | 12 | (defun parse-string (string) 13 | "Parse a docstring into a documentation node using the current markup format." 14 | (pandocl:parse-string string *current-markup-format*)) 15 | -------------------------------------------------------------------------------- /src/quickstart.lisp: -------------------------------------------------------------------------------- 1 | (in-package :codex) 2 | 3 | (defparameter +manifest-template+ 4 | "(:docstring-markup-format :scriba 5 | :systems (:~A) 6 | :documents ((:title ~S 7 | :authors (~S) 8 | :output-format (:type :multi-html 9 | :template :minima) 10 | :sources (\"manual.scr\"))))") 11 | 12 | (defparameter +manual-template+ 13 | "@begin(section) 14 | @title(Overview) 15 | 16 | This is where you explain what your project does. 17 | 18 | @end(section) 19 | 20 | @begin(section) 21 | @title(API Reference) 22 | 23 | And this is where you list your project's functionality. Read the tutorial to 24 | learn how to do this. 25 | 26 | @end(section) 27 | ") 28 | 29 | (defun clean-author-name (author) 30 | (string-trim '(#\Space) 31 | (ppcre:regex-replace "<.+>" author ""))) 32 | 33 | (defun quickstart (system-name) 34 | "Create a documentation folder, manifest, and a sample file for the given 35 | system." 36 | (let* ((system (asdf:find-system system-name)) 37 | (title (string-capitalize (asdf:component-name system))) 38 | (author (clean-author-name (asdf:system-author system))) 39 | (docs-directory (asdf:system-relative-pathname system-name #p"docs/")) 40 | (manifest-pathname (merge-pathnames #p"manifest.lisp" 41 | docs-directory)) 42 | (manual-pathname (merge-pathnames #p"manual.scr" 43 | docs-directory))) 44 | (ensure-directories-exist docs-directory) 45 | (with-open-file (stream manifest-pathname 46 | :direction :output 47 | :if-exists :error 48 | :if-does-not-exist :create) 49 | (format stream +manifest-template+ 50 | (string-downcase (symbol-name system-name)) 51 | title 52 | author)) 53 | (with-open-file (stream manual-pathname 54 | :direction :output 55 | :if-exists :error 56 | :if-does-not-exist :create) 57 | (write-string +manual-template+ stream)) 58 | t)) 59 | -------------------------------------------------------------------------------- /t/codex.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage codex-test 3 | (:use :cl :fiveam)) 4 | (in-package :codex-test) 5 | 6 | ;;; Constants 7 | 8 | (defparameter +doc-build-directory+ 9 | (asdf:system-relative-pathname :codex-test-system 10 | #p"docs/build/")) 11 | 12 | ;;; Tests 13 | 14 | (def-suite tests) 15 | (in-suite tests) 16 | 17 | (test manifest-error 18 | (signals codex.error:unsupported-output-format 19 | (codex.manifest:parse-manifest 20 | (asdf:system-relative-pathname :codex-test 21 | #p"t/manifests/output.lisp"))) 22 | (signals codex.error:template-error 23 | (let* ((manifest-pathname #p"t/manifests/template.lisp") 24 | (manifest (codex.manifest:parse-manifest 25 | (asdf:system-relative-pathname :codex-test 26 | manifest-pathname))) 27 | (build-directory (asdf:system-relative-pathname :codex-test-system 28 | #p"docs/"))) 29 | (codex::build-manifest manifest build-directory))) 30 | (signals codex.error:manifest-error 31 | ;; Try to document a system that certainly has no manifest 32 | (codex:document :alexandria))) 33 | 34 | (test set-up 35 | ;; Ensure the test system's docs build directory is empty 36 | (finishes 37 | (when (probe-file +doc-build-directory+) 38 | (uiop:delete-directory-tree +doc-build-directory+ 39 | :validate t)))) 40 | 41 | (test document-test-system 42 | (finishes 43 | (codex:document :codex-test-system))) 44 | 45 | (test files-exist 46 | (let ((files (list #p"doc-a/html/section-a.html" 47 | #p"doc-a/html/static/style.css" 48 | #p"doc-a/html/static/highlight.js" 49 | #p"doc-a/html/static/highlight.css" 50 | #p"doc-a/html/test.png"))) 51 | (loop for file in files do 52 | (is-true 53 | (probe-file (merge-pathnames file 54 | +doc-build-directory+)))))) 55 | 56 | (test macros-work 57 | ;; Ensure all the names are right 58 | (is-false 59 | (search "No node with name" 60 | (uiop:read-file-string 61 | (merge-pathnames #p"doc-a/html/section-a.html" 62 | +doc-build-directory+)))) 63 | ;; Ensure all nodes are supported 64 | (is-false 65 | (search "Unsupported node type" 66 | (uiop:read-file-string 67 | (merge-pathnames #p"doc-a/html/section-a.html" 68 | +doc-build-directory+)))) 69 | (is-true 70 | (search "http://l1sp.org/cl/defpackage" 71 | (uiop:read-file-string 72 | (merge-pathnames #p"doc-a/html/section-a.html" 73 | +doc-build-directory+)))) 74 | (is-false 75 | (search "No node with name" 76 | (uiop:read-file-string 77 | (merge-pathnames #p"doc-b/html/section-a.html" 78 | +doc-build-directory+)))) 79 | (is-false 80 | (search "Unsupported node type" 81 | (uiop:read-file-string 82 | (merge-pathnames #p"doc-b/html/section-a.html" 83 | +doc-build-directory+)))) 84 | (is-true 85 | (search "No node type with name" 86 | (uiop:read-file-string 87 | (merge-pathnames #p"doc-b/html/section-b.html" 88 | +doc-build-directory+)))) 89 | (is-true 90 | (search "No node with name" 91 | (uiop:read-file-string 92 | (merge-pathnames #p"doc-b/html/section-b.html" 93 | +doc-build-directory+))))) 94 | 95 | (run! 'tests) 96 | -------------------------------------------------------------------------------- /t/manifests/output.lisp: -------------------------------------------------------------------------------- 1 | ;;;; A manifest with an unsupported output format 2 | (:docstring-markup-format :scriba 3 | :systems (:codex-test-system) 4 | :documents ((:title "Test" 5 | :authors ("Test") 6 | :output-format (:type :some-output-format) 7 | :sources ("sec-a.scr")))) 8 | -------------------------------------------------------------------------------- /t/manifests/template.lisp: -------------------------------------------------------------------------------- 1 | ;;;; A manifest with an unsupported template 2 | (:docstring-markup-format :scriba 3 | :systems (:codex-test-system) 4 | :documents ((:title "Test" 5 | :authors ("Test") 6 | :output-format (:type :multi-html 7 | :template :no-such-template) 8 | :sources ("sec-a.scr")))) 9 | -------------------------------------------------------------------------------- /t/test-system/codex-test-system.asd: -------------------------------------------------------------------------------- 1 | (defsystem codex-test-system 2 | :author "Fernando Borretti " 3 | :license "MIT" 4 | :description "Test system that's loaded and parsed." 5 | :depends-on () 6 | :components ((:file "test-system"))) 7 | -------------------------------------------------------------------------------- /t/test-system/docs/manifest.lisp: -------------------------------------------------------------------------------- 1 | (:docstring-markup-format :scriba 2 | :systems (:codex-test-system) 3 | :documents ((:title "Doc A" 4 | :authors ("Fernando Borretti") 5 | :output-format (:type :multi-html 6 | :template :minima) 7 | :sources ("sec-a.scr" 8 | "sec-b.scr")) 9 | (:title "Doc B" 10 | :authors ("Fernando Borretti") 11 | :output-format (:type :multi-html 12 | :template :gamma) 13 | :sources ("sec-a.scr" 14 | "sec-b.scr")))) 15 | -------------------------------------------------------------------------------- /t/test-system/docs/sec-a.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Section A) 3 | 4 | @image[src=test.png]() 5 | 6 | Here's a link to the hyperspec: @cl:spec(defpackage). 7 | 8 | @begin(section) 9 | @title(Nodes) 10 | 11 | @cl:with-package[name="codex-test-system"]( 12 | @cl:doc(function func) 13 | @cl:doc(macro mac) 14 | @cl:doc(generic test-method) 15 | @cl:doc(method test-method (tc test-class) a) 16 | @cl:doc(variable var) 17 | @cl:doc(struct struct) 18 | @cl:doc(class test-class) 19 | @cl:doc(condition my-error) 20 | @cl:doc(type custom-string) 21 | 22 | @cl:doc(cfunction printf) 23 | @cl:doc(ctype size-t) 24 | @cl:doc(cstruct cstruct) 25 | @cl:doc(cunion cunion) 26 | @cl:doc(cenum nums) 27 | @cl:doc(cbitfield bits) 28 | ) 29 | 30 | @end(section) 31 | 32 | @begin(section) 33 | @title(Markup) 34 | 35 | @begin(section) 36 | @title(Subsection) 37 | 38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 39 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 40 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 41 | 42 | @begin(section) 43 | @title(Sub-Subsection) 44 | 45 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 46 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 47 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 48 | 49 | @begin(section) 50 | @title(Sub-Sub-Subsection) 51 | 52 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 53 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 54 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 55 | 56 | @end(section) 57 | 58 | @end(section) 59 | 60 | @end(section) 61 | 62 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 63 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 64 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 65 | 66 | @begin(list) 67 | @item(A list.) 68 | @item(With two items.) 69 | @end(list) 70 | 71 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 72 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 73 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 74 | 75 | @begin(enum) 76 | @item(An ordered list.) 77 | @item(With three items.) 78 | @item(Last one.) 79 | @end(enum) 80 | 81 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 82 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 83 | nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 84 | 85 | @begin(deflist) 86 | @term(This is a term) 87 | @def(And this is its definition) 88 | @end(deflist) 89 | 90 | @begin(table) 91 | 92 | @begin(row) 93 | @cell(a) 94 | @cell(b) 95 | @end(row) 96 | 97 | @begin(row) 98 | @cell(1) 99 | @cell(2) 100 | @end(row) 101 | 102 | @begin[lang=lisp](code) 103 | @begin(verb) 104 | (let ((x 1)) 105 | (funcall #'func x)) 106 | @end(verb) 107 | @end(code) 108 | 109 | @end(table) 110 | 111 | @end(section) 112 | 113 | @end(section) 114 | -------------------------------------------------------------------------------- /t/test-system/docs/sec-b.scr: -------------------------------------------------------------------------------- 1 | @begin(section) 2 | @title(Section B) 3 | 4 | @cl:with-package[name="codex-test-system"]( 5 | @cl:doc(foo bar) 6 | @cl:doc(function foobar) 7 | ) 8 | 9 | @end(section) 10 | -------------------------------------------------------------------------------- /t/test-system/docs/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/t/test-system/docs/test.png -------------------------------------------------------------------------------- /t/test-system/test-system.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage codex-test-system 3 | (:use :cl) 4 | (:documentation "docstring")) 5 | (in-package :codex-test-system) 6 | 7 | ;;; Common Lisp nodes 8 | 9 | (defun func (a b &optional (c "") (l 1)) 10 | "docstring" 11 | (declare (ignore a b c l)) 12 | t) 13 | 14 | (defmacro mac (a b c) 15 | "docstring" 16 | (declare (ignore a b c)) 17 | t) 18 | 19 | (defparameter var t 20 | "docstring") 21 | 22 | (defstruct struct 23 | "docstring" 24 | a 25 | (b "test" :type string) 26 | (c 1)) 27 | 28 | (defclass test-class () 29 | ((first-slot :accessor first-slot 30 | :initarg :first-slot 31 | :documentation "docstring") 32 | (second-slot :reader second-slot 33 | :reader s-slot 34 | :initarg :second-slot 35 | :documentation "docstring") 36 | (unexported-slot :reader unexported-slot 37 | :initarg :unexported-slot 38 | :documentation "docstring")) 39 | (:documentation "docstring")) 40 | 41 | (defgeneric test-method (obj a) 42 | (:documentation "docstring")) 43 | 44 | (defmethod test-method ((tc test-class) a) 45 | "docstring" 46 | (declare (ignore tc a)) 47 | t) 48 | 49 | (define-condition my-error () 50 | ((first-slot :accessor first-slot 51 | :initarg :first-slot 52 | :documentation "docstring")) 53 | (:documentation "docstring")) 54 | 55 | (deftype custom-string (val) 56 | "docstring" 57 | (string= val "my-string")) 58 | 59 | ;;; CFFI nodes 60 | 61 | (cffi:defcfun printf :int 62 | "docstring" 63 | (control :string) &rest) 64 | 65 | (cffi:defctype size-t :unsigned-long "docstring") 66 | 67 | (cffi:defcstruct cstruct 68 | "docstring" 69 | (a :int) 70 | (b :double)) 71 | 72 | (cffi:defcunion cunion 73 | "docstring" 74 | (a :int) 75 | (b :double)) 76 | 77 | (cffi:defcenum nums "docstring" :a :b (:c 3)) 78 | 79 | (cffi:defbitfield bits "docstring" :a :b (:c #x0200)) 80 | -------------------------------------------------------------------------------- /templates/gamma/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}{% endblock %} 8 | 9 | {% block extra-head %}{% endblock %} 10 | 11 | 12 | {% block content %}{% endblock %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/gamma/document.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | {{ title }} 5 | {% endblock %} 6 | 7 | {% block extra-head %} 8 | 9 | 10 | 11 | {% endblock %} 12 | 13 | {% block content %} 14 |
15 |

{{ title }}

16 | {{ content | safe }} 17 |
18 |
19 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /templates/gamma/section.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | {{ section-title }} – {{ document-title }} 5 | {% endblock %} 6 | 7 | {% block extra-head %} 8 | 9 | 10 | 11 | 34 | {% endblock %} 35 | 36 | {% block content %} 37 |

{{ document-title }}

38 |
39 | 42 |
43 |
44 |

{{ section-title }}

45 |
46 |
47 | {{ content | safe }} 48 |
49 |
50 |
51 |
52 |
53 | Created with Codex. 54 |
55 |
56 | 59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /templates/gamma/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #444; 3 | background: #FAFBFB; 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | 6 | display: flex; 7 | min-height: 100vh; 8 | flex-direction: column; 9 | } 10 | 11 | /* Layout */ 12 | 13 | .doc-title { 14 | height: 120px; 15 | line-height: 120px; 16 | padding-left: 70px; 17 | font-size: 36px; 18 | } 19 | 20 | article { 21 | width: 80%; 22 | margin: 0 auto; 23 | 24 | flex: 1; 25 | display: flex; 26 | flex-direction: row; 27 | } 28 | 29 | article aside { 30 | width: 180px; 31 | margin-top: 14px; 32 | margin-bottom: 14px; 33 | padding-top: 10px; 34 | padding-left: 15px; 35 | 36 | background: #FCFDFF; 37 | 38 | border-radius: 4px 0 0 4px; 39 | box-shadow: 0 0 3px #ccc; 40 | } 41 | 42 | article main { 43 | flex: 1; 44 | padding: 25px; 45 | 46 | background: white; 47 | 48 | border-radius: 4px; 49 | box-shadow: 0 0 3px #ccc; 50 | } 51 | 52 | article header { 53 | border-bottom: 1px solid #ccc; 54 | margin-bottom: 25px; 55 | } 56 | 57 | article header .section-title { 58 | text-align: center; 59 | font-size: 36px; 60 | padding-bottom: 25px; 61 | } 62 | 63 | /* Links */ 64 | 65 | a { 66 | text-decoration: none; 67 | color: #3498db; 68 | } 69 | 70 | /* Content */ 71 | 72 | .content { 73 | font-size: 16px; 74 | font-weight: 400; 75 | } 76 | 77 | .content p code { 78 | font-size: 16px; 79 | } 80 | 81 | .content > *, .content blockquote > * { 82 | margin-bottom: 25px; 83 | } 84 | 85 | .content h1 { 86 | font-size: 36px; 87 | 88 | margin-top: 36px; 89 | padding-bottom: 10px; 90 | 91 | border-bottom: 1px solid #eee; 92 | } 93 | 94 | .content h2 { 95 | font-size: 28px; 96 | 97 | margin-top: 28px; 98 | padding-bottom: 8px; 99 | 100 | border-bottom: 1px solid #eee; 101 | } 102 | 103 | .content h3 { 104 | font-size: 24px; 105 | 106 | margin-top: 24px; 107 | } 108 | 109 | .content h4 { 110 | font-size: 20px; 111 | 112 | margin-top: 20; 113 | } 114 | 115 | .content h5 { 116 | font-size: 1em; 117 | 118 | margin-top: 18px; 119 | } 120 | 121 | .content h6 { 122 | font-size: 1em; 123 | 124 | margin-top: 18px; 125 | } 126 | 127 | .content ul li, .content ol li, .content dl dd { 128 | margin-left: 30px; 129 | } 130 | 131 | .content dl dt { 132 | font-weight: bold; 133 | } 134 | 135 | table { 136 | border-collapse: collapse; 137 | margin: 0 auto; 138 | } 139 | 140 | td { 141 | padding: 6px; 142 | min-width: 100px; 143 | } 144 | 145 | tr:first-child td { 146 | text-align: center; 147 | font-weight: bold; 148 | 149 | border-bottom: 1px solid #ccc; 150 | } 151 | 152 | blockquote { 153 | padding-left: 40px; 154 | border-left: 2px solid #ccc; 155 | } 156 | 157 | img { 158 | display: block; 159 | max-width: 100%; 160 | margin: 0 auto; 161 | } 162 | 163 | /* Table of Contents */ 164 | 165 | .toc li { 166 | list-style-type: none; 167 | } 168 | 169 | .toc a { 170 | display: block; 171 | margin-bottom: 5px; 172 | font-size: 13px; 173 | } 174 | 175 | /* Footer */ 176 | 177 | footer { 178 | } 179 | 180 | footer .info { 181 | font-size: 16px; 182 | 183 | height: 100px; 184 | line-height: 100px; 185 | text-align: center; 186 | } 187 | -------------------------------------------------------------------------------- /templates/minima/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}{% endblock %} 8 | 9 | {% block extra-head %}{% endblock %} 10 | 11 | 12 | {% block content %}{% endblock %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/minima/document.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | {{ title }} 5 | {% endblock %} 6 | 7 | {% block extra-head %} 8 | 9 | 10 | 11 | {% endblock %} 12 | 13 | {% block content %} 14 |
15 |

{{ title }}

16 | {{ content | safe }} 17 |
18 |
19 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /templates/minima/section.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | {{ section-title }} – {{ document-title }} 5 | {% endblock %} 6 | 7 | {% block extra-head %} 8 | 9 | 10 | 11 | 39 | {% endblock %} 40 | 41 | {% block content %} 42 |

{{ document-title }} » {{ section-title }}

43 |
44 | 47 |
48 | {{ content | safe }} 49 |
50 |
51 |
52 |
53 | Created with Codex. 54 |
55 |
56 | 59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /templates/minima/style.css: -------------------------------------------------------------------------------- 1 | /* Template style */ 2 | 3 | @import url(http://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic); 4 | @import url(http://fonts.googleapis.com/css?family=Source+Serif+Pro:400,600,700); 5 | @import url(http://fonts.googleapis.com/css?family=Source+Code+Pro); 6 | 7 | body { 8 | font-family: 'Source Serif Pro', serif; 9 | color: #333; 10 | font-weight: 400; 11 | font-size: 18px; 12 | 13 | background: white; 14 | } 15 | 16 | /* General layout */ 17 | 18 | body { 19 | width: 100%; 20 | display: flex; 21 | min-height: 100vh; 22 | flex-direction: column; 23 | } 24 | 25 | main { 26 | width: 75%; 27 | min-height: 100vh; 28 | float: left; 29 | padding: 0 35px; 30 | } 31 | 32 | article { 33 | flex: 1; 34 | margin-bottom: 100px; 35 | } 36 | 37 | /* Heading style */ 38 | 39 | h1, h2, h3, h4, h5, h6 { 40 | font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 41 | } 42 | 43 | .doc-title { 44 | margin: 25px 75px; 45 | } 46 | 47 | .codex-document, .codex-section { 48 | width: 60%; 49 | margin: 0 auto; 50 | } 51 | 52 | .codex-document-title { 53 | width: 60%; 54 | margin: 0 auto; 55 | } 56 | 57 | main > *:first-child { 58 | margin-top: 0; 59 | } 60 | 61 | main h1 { 62 | font-size: 36px; 63 | 64 | margin-top: 36px; 65 | padding-bottom: 10px; 66 | 67 | border-bottom: 1px solid #eee; 68 | } 69 | 70 | main h2 { 71 | font-size: 28px; 72 | 73 | margin-top: 28px; 74 | padding-bottom: 8px; 75 | 76 | border-bottom: 1px solid #eee; 77 | } 78 | 79 | main h3 { 80 | font-size: 24px; 81 | 82 | margin-top: 24px; 83 | } 84 | 85 | main h4 { 86 | font-size: 20px; 87 | 88 | margin-top: 20; 89 | } 90 | 91 | main h5 { 92 | font-size: 1em; 93 | 94 | margin-top: 18px; 95 | } 96 | 97 | main h6 { 98 | font-size: 1em; 99 | 100 | margin-top: 18px; 101 | } 102 | 103 | /* Error style */ 104 | 105 | .codex-error, .codex-warning { 106 | padding: 15px; 107 | border: 1px solid #ccc; 108 | border-radius: 4px; 109 | } 110 | 111 | /* Separation between elements */ 112 | 113 | main > *, dd > *, blockquote > * { 114 | margin-bottom: 15px; 115 | } 116 | 117 | /* Lists */ 118 | 119 | dd, li { 120 | margin-left: 40px; 121 | } 122 | 123 | dt { 124 | font-weight: bold; 125 | margin-top: 15px; 126 | } 127 | 128 | li { 129 | margin-top: 15px; 130 | } 131 | 132 | /* Code */ 133 | 134 | code { 135 | font-family: Consolas, 'Liberation Mono', Courier, monospace; 136 | } 137 | 138 | pre > code { 139 | font-size: 13px; 140 | } 141 | 142 | /* Link Style */ 143 | 144 | a { 145 | color: #AD3108; 146 | text-decoration: none; 147 | } 148 | 149 | /* Quote style */ 150 | 151 | blockquote { 152 | padding-left: 25px; 153 | border-left: 1px solid #ccc; 154 | } 155 | 156 | /* Table style */ 157 | 158 | table { 159 | border-collapse: collapse; 160 | } 161 | 162 | td { 163 | padding: 15px; 164 | } 165 | 166 | table, td, th { 167 | border: 1px solid black; 168 | } 169 | 170 | /* Images and figures */ 171 | 172 | img { 173 | max-width: 100%; 174 | } 175 | 176 | /* Table of Contents */ 177 | 178 | .toc { 179 | width: 25%; 180 | float: left; 181 | padding: 0 35px; 182 | } 183 | 184 | .toc a { 185 | color: #333; 186 | text-decoration: none; 187 | position: relative; 188 | } 189 | 190 | .toc li { 191 | list-style-type: none; 192 | font-size: 19px; 193 | margin-left: 0; 194 | margin-bottom: 10px; 195 | } 196 | 197 | /* Footer */ 198 | 199 | footer { 200 | height: 120px; 201 | line-height: 120px; 202 | background: #f9f9f9; 203 | } 204 | 205 | footer .info { 206 | margin: 0 75px; 207 | text-align: right; 208 | } 209 | -------------------------------------------------------------------------------- /templates/static/highlight-lisp/README.md: -------------------------------------------------------------------------------- 1 | highlight-lisp - Common Lisp syntax highlighter 2 | =============================================== 3 | This is a syntax highlighter for Common Lisp written in Javascript. It is 4 | completely themable via CSS (themes included). 5 | 6 | The purpose of this is to make it really easy to embed beautiful Common Lisp 7 | code into a website with minimal effort. 8 | 9 | [See the demo!](http://orthecreedence.github.com/highlight-lisp/) 10 | 11 | Usage 12 | ----- 13 | Usage is simple. You include `highlight-lisp.js`, link to one of the CSS themes, 14 | and call one of highlight-lisp's highlighting functions: 15 | 16 | ```html 17 | 19 | 20 | 21 | 22 | ... 23 | 24 | 25 |
(defun test-syntax-highlighter ()
 26 |   "Docstring explaining what this function does."
 27 |   (let ((hash (make-hash-table :test #'equal)))
 28 |     ...))
29 | ``` 30 | 31 | Once the HTML is set up, there are a few ways to initialize highlighting: 32 | 33 | ```js 34 | // automatically highlight all ... blocks 35 | HighlightLisp.highlight_auto(); 36 | 37 | // specify a custom class name (instead of "lisp"): 38 | HighlightLisp.highlight_auto({className: 'common-lisp'}); 39 | 40 | // highlight *every* code block 41 | HighlightLisp.highlight_auto({className: null}); 42 | 43 | // manually highlight a code block 44 | var code = document.getElementById('my-code-element'); 45 | HighlightLisp.highlight_element(code); 46 | ``` 47 | 48 | What gets highlighted 49 | --------------------- 50 | - **Functions** 51 | CSS class `function` 52 | Anything starting with `(`: `(my-function ...)` 53 | - **Known functions** 54 | CSS class `function known` 55 | Any function known by the highlighter: things like `make-hash-table`, `when`, 56 | `format`, etc 57 | - **Special functions** 58 | CSS class `function known special` 59 | Mainly `let`, `let\*`, `lambda`. 60 | - **Symbol functions** 61 | CSS class `function symbol` 62 | Example: `#'my-function` 63 | - **Known symbol functions** 64 | CSS class `function symbol known` 65 | Examples: `#'equalp`, `#'format` 66 | - **Keywords** 67 | CSS class `keyword` 68 | Anything starting with `:` like `:this-is-a-keyword ` 69 | - **Known keywords** 70 | CSS class `keyword known` 71 | Known keywords are things like `:hash-keys`, `:supersede`, etc. 72 | - **Symbols** 73 | CSS class `symbol` 74 | Anything starting with `'`: `'my-symbol` 75 | - **Lambda-list operators** 76 | CSS class `lambda-list` 77 | Things like `&key`, `&body`, etc. 78 | - **Numbers** 79 | CSS class `number` 80 | Any numbers: `69`, `-82.4`, `#xF047`, `#b11010` 81 | - **Integers** 82 | CSS class `number integer` 83 | Simple numbers: `42`, `867`, etc. (no decimals) 84 | - **Ratios** 85 | CSS class `number ratio` 86 | Examples: `80/9`, `23/4` 87 | - **Floats** 88 | CSS class `number float` 89 | Numbers with a decimal: `+47.82112`, `32.9` `3.` `.009` 90 | - **Hex** 91 | CSS class `number hex` 92 | Hex numbers: `#x8090`, `#xc001` 93 | - **Binary** 94 | CSS class `number binary` 95 | Example: `#b01101` 96 | - **Variables** 97 | By themselves, variables remain unhighlighted 98 | - **Known variables** 99 | CSS class `variable known` 100 | Examples: `*package*`, `*standard-output*`, etc 101 | - **Global variables** 102 | CSS class `variable global` 103 | Any symbol surrounded by `\*`: `*main-datastore*`, `*my-thread-local*`, etc 104 | - **Constants** 105 | CSS class `variable constant` 106 | Any symbol surrounded by `+`: `+dt+`, `+contant-time+`, etc 107 | - **nil/t** 108 | CSS class `nil` 109 | Any standalone `nil` or `t` will get this class 110 | - **Comments** 111 | CSS class `comment` 112 | Example: `; this is a comment` 113 | - **Strings** 114 | CSS class `string` 115 | Anthing inside `"`: `"This is a string."` 116 | - **Parens** 117 | CSS class `list` 118 | May be overkill, but any `(` or `)` characters are classified. 119 | 120 | On that note, things that *don't get highlighted/aren't properly highlighted*: 121 | 122 | - Variables...things like `let` bindings or other symols within code that would 123 | be interpreted as variables. Highlighting these would most likely be prohibitive 124 | in terms of time (not the mention the return on investment). Feel free to patch! 125 | - Some number notations. For instance `0.44d0`. 126 | - Multi-line comments `#| ... |#` are unsupported 127 | - Many constants (such as `pi`, `internal-time-units-per-second`) are classified 128 | as functions, not known variables. This is because I pulled the list out of my 129 | vim highlight script, and couldn't find a list of "Common Lisp standard 130 | variables" to cross reference with. I pulled out the ones I know of and put them 131 | into the known variables list, but there are no doubt more. If you see something 132 | that is a known variable but gets treated as a known function, please open a 133 | github issue. 134 | 135 | Why 136 | --- 137 | > Aren't there a bunch of Javascript syntax highlighters out there already? 138 | 139 | Yes, but truth be told, most ignore lisp. You can write custom parsers for some 140 | of them, but the APIs they provide didn't work well enough for me. [highlight.js](http://softwaremaniacs.org/soft/highlight/en/) 141 | has a very nice lisp highlighting mode, along with really nice themes, but I 142 | wanted more control over the process. 143 | 144 | For instance, `highlight-lisp` started as a [SyntaxHighlighter](http://alexgorbatchev.com/SyntaxHighlighter/) 145 | brush, but I quickly realized that because of the limitations of Javascript not 146 | allowing real [lookbehind regular expressions](http://www.regular-expressions.info/lookaround.html), 147 | I needed more direct control over the search/replace process. 148 | 149 | What I discovered was that given the proper tools, parsing lisp is *easy* 150 | (in fact, a cake walk after just releasing [markdown.cl](https://github.com/orthecreedence/markdown.cl)) 151 | and there's no need for a big highlighting framework. You plug in some regexes, 152 | slap some tags around certain things, and call it a day. 153 | 154 | License 155 | ------- 156 | As always, MIT. 157 | 158 | 159 | -------------------------------------------------------------------------------- /templates/static/highlight-lisp/highlight-lisp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common Lisp syntax highlighter 3 | * 4 | * @version 0.1.1 5 | * @author Andrew "Danger" Lyon 6 | * @copyright Lyon Bros. Enterprises, LLC 7 | * @licence MIT 8 | */ 9 | var highlight_lisp = function() { 10 | // all of the following functions were pulled straight from my syntax/lisp.vim 11 | // file in my vim directory. 12 | var funcs = 13 | '\\* find-method pprint-indent find-package pprint-linear find-restart ' + 14 | 'pprint-logical-block \\+ find-symbol pprint-newline finish-output ' + 15 | 'pprint-pop first pprint-tab - fixnum pprint-tabular / flet prin1 // float ' + 16 | 'prin1-to-string /// float-digits princ /= float-precision princ-to-string 1\\+ ' + 17 | 'float-radix print 1- float-sign print-not-readable < floating-point-inexact ' + 18 | 'print-not-readable-object <= floating-point-invalid-operation print-object = ' + 19 | 'floating-point-overflow print-unreadable-object > floating-point-underflow ' + 20 | 'probe-file >= floatp proclaim abort floor prog abs fmakunbound prog\\* access ' + 21 | 'force-output prog1 acons format prog2 acos formatter progn acosh fourth ' + 22 | 'program-error add-method fresh-line progv adjoin fround provide adjust-array ' + 23 | 'ftruncate psetf adjustable-array-p ftype psetq allocate-instance funcall push ' + 24 | 'alpha-char-p function pushnew alphanumericp function-keywords putprop and ' + 25 | 'function-lambda-expression quote append functionp random apply gbitp ' + 26 | 'random-state applyhook gcd random-state-p apropos generic-function rassoc ' + 27 | 'apropos-list gensym rassoc-if aref gentemp rassoc-if-not arithmetic-error get ' + 28 | 'ratio arithmetic-error-operands get-decoded-time rational ' + 29 | 'arithmetic-error-operation get-dispatch-macro-character rationalize array ' + 30 | 'get-internal-real-time rationalp array-dimension get-internal-run-time read ' + 31 | 'array-dimension-limit get-macro-character read-byte array-dimensions ' + 32 | 'get-output-stream-string read-char array-displacement get-properties ' + 33 | 'read-char-no-hang array-element-type get-setf-expansion read-delimited-list ' + 34 | 'array-has-fill-pointer-p get-setf-method read-eval-print array-in-bounds-p ' + 35 | 'get-universal-time read-from-string array-rank getf read-line array-rank-limit ' + 36 | 'gethash read-preserving-whitespace array-row-major-index go read-sequence ' + 37 | 'array-total-size graphic-char-p reader-error array-total-size-limit handler-bind ' + 38 | 'readtable arrayp handler-case readtable-case ash hash-table readtablep asin ' + 39 | 'hash-table-count real asinh hash-table-p realp assert hash-table-rehash-size ' + 40 | 'realpart assoc hash-table-rehash-threshold reduce assoc-if hash-table-size ' + 41 | 'reinitialize-instance assoc-if-not hash-table-test rem atan host-namestring ' + 42 | 'remf atanh identity remhash atom if remove base-char if-exists ' + 43 | 'remove-duplicates base-string ignorable remove-if bignum ignore remove-if-not ' + 44 | 'bit ignore-errors remove-method bit-and imagpart remprop bit-andc1 import ' + 45 | 'rename-file bit-andc2 in-package rename-package bit-eqv in-package replace ' + 46 | 'bit-ior incf require bit-nand initialize-instance rest bit-nor inline restart ' + 47 | 'bit-not input-stream-p restart-bind bit-orc1 inspect restart-case bit-orc2 ' + 48 | 'int-char restart-name bit-vector integer return bit-vector-p ' + 49 | 'integer-decode-float return-from bit-xor integer-length revappend block ' + 50 | 'integerp reverse boole interactive-stream-p room boole-1 intern rotatef ' + 51 | 'boole-2 round boole-and intersection ' + 52 | 'row-major-aref boole-andc1 invalid-method-error rplaca boole-andc2 ' + 53 | 'invoke-debugger rplacd boole-c1 invoke-restart safety boole-c2 ' + 54 | 'invoke-restart-interactively satisfies boole-clr isqrt sbit boole-eqv keyword ' + 55 | 'scale-float boole-ior keywordp schar boole-nand labels search boole-nor ' + 56 | 'second boole-orc1 lambda-list-keywords sequence boole-orc2 ' + 57 | 'lambda-parameters-limit serious-condition boole-set last set boole-xor lcm ' + 58 | 'set-char-bit boolean ldb set-difference both-case-p ldb-test ' + 59 | 'set-dispatch-macro-character boundp ldiff set-exclusive-or break ' + 60 | 'least-negative-double-float set-macro-character broadcast-stream ' + 61 | 'least-negative-long-float set-pprint-dispatch broadcast-stream-streams ' + 62 | 'least-negative-normalized-double-float set-syntax-from-char built-in-class ' + 63 | 'least-negative-normalized-long-float setf butlast ' + 64 | 'least-negative-normalized-short-float setq byte ' + 65 | 'least-negative-normalized-single-float seventh byte-position ' + 66 | 'least-negative-short-float shadow byte-size least-negative-single-float ' + 67 | 'shadowing-import call-arguments-limit least-positive-double-float ' + 68 | 'shared-initialize call-method least-positive-long-float shiftf ' + 69 | 'call-next-method least-positive-normalized-double-float short-float capitalize ' + 70 | 'least-positive-normalized-long-float short-float-epsilon car ' + 71 | 'least-positive-normalized-short-float short-float-negative-epsilon case ' + 72 | 'least-positive-normalized-single-float short-site-name catch ' + 73 | 'least-positive-short-float signal ccase least-positive-single-float ' + 74 | 'signed-byte cdr length signum ceiling simple-condition cell-error ' + 75 | 'simple-array cell-error-name lisp simple-base-string cerror ' + 76 | 'lisp-implementation-type simple-bit-vector change-class ' + 77 | 'lisp-implementation-version simple-bit-vector-p char list ' + 78 | 'simple-condition-format-arguments char-bit list\\* ' + 79 | 'simple-condition-format-control char-bits list-all-packages simple-error ' + 80 | 'char-bits-limit list-length simple-string char-code listen simple-string-p ' + 81 | 'char-code-limit listp simple-type-error char-control-bit load simple-vector ' + 82 | 'char-downcase load-logical-pathname-translations simple-vector-p char-equal ' + 83 | 'load-time-value simple-warning char-font locally sin char-font-limit log ' + 84 | 'single-flaot-epsilon char-greaterp logand single-float char-hyper-bit logandc1 ' + 85 | 'single-float-epsilon char-int logandc2 single-float-negative-epsilon ' + 86 | 'char-lessp logbitp sinh char-meta-bit logcount sixth char-name logeqv sleep ' + 87 | 'char-not-equal logical-pathname slot-boundp char-not-greaterp ' + 88 | 'logical-pathname-translations slot-exists-p char-not-lessp logior ' + 89 | 'slot-makunbound char-super-bit lognand slot-missing char-upcase lognor ' + 90 | 'slot-unbound char/= lognot slot-value char< logorc1 software-type char<= ' + 91 | 'logorc2 software-version char= logtest some char> logxor sort char>= ' + 92 | 'long-float space character long-float-epsilon special characterp ' + 93 | 'long-float-negative-epsilon special-form-p check-type long-site-name ' + 94 | 'special-operator-p cis loop speed class loop-finish sqrt class-name ' + 95 | 'lower-case-p stable-sort class-of machine-instance standard clear-input ' + 96 | 'machine-type standard-char clear-output machine-version standard-char-p close ' + 97 | 'macro-function standard-class clrhash macroexpand standard-generic-function ' + 98 | 'code-char macroexpand-1 standard-method coerce macroexpand-l standard-object ' + 99 | 'commonp macrolet step compilation-speed make-array storage-condition compile ' + 100 | 'make-array store-value compile-file make-broadcast-stream stream ' + 101 | 'compile-file-pathname make-char stream-element-type compiled-function ' + 102 | 'make-concatenated-stream stream-error compiled-function-p make-condition ' + 103 | 'stream-error-stream compiler-let make-dispatch-macro-character ' + 104 | 'stream-external-format compiler-macro make-echo-stream streamp ' + 105 | 'compiler-macro-function make-hash-table streamup complement make-instance ' + 106 | 'string complex make-instances-obsolete string-capitalize complexp make-list ' + 107 | 'string-char compute-applicable-methods make-load-form string-char-p ' + 108 | 'compute-restarts make-load-form-saving-slots string-downcase concatenate ' + 109 | 'make-method string-equal concatenated-stream make-package string-greaterp ' + 110 | 'concatenated-stream-streams make-pathname string-left-trim cond ' + 111 | 'make-random-state string-lessp condition make-sequence string-not-equal ' + 112 | 'conjugate make-string string-not-greaterp cons make-string-input-stream ' + 113 | 'string-not-lessp consp make-string-output-stream string-right-strim constantly ' + 114 | 'make-symbol string-right-trim constantp make-synonym-stream string-stream ' + 115 | 'continue make-two-way-stream string-trim control-error makunbound ' + 116 | 'string-upcase copy-alist map string/= copy-list map-into string< ' + 117 | 'copy-pprint-dispatch mapc string<= copy-readtable mapcan string= copy-seq ' + 118 | 'mapcar string> copy-structure mapcon string>= copy-symbol maphash stringp ' + 119 | 'copy-tree mapl structure cos maplist structure-class cosh mask-field ' + 120 | 'structure-object count max style-warning count-if member sublim count-if-not ' + 121 | 'member-if sublis ctypecase member-if-not subseq debug merge subsetp decf ' + 122 | 'merge-pathname subst declaim merge-pathnames subst-if declaration method ' + 123 | 'subst-if-not declare method-combination substitute decode-float ' + 124 | 'method-combination-error substitute-if decode-universal-time method-qualifiers ' + 125 | 'substitute-if-not defclass min subtypep defconstant minusp svref defgeneric ' + 126 | 'mismatch sxhash define-compiler-macro mod symbol define-condition ' + 127 | 'most-negative-double-float symbol-function define-method-combination ' + 128 | 'most-negative-fixnum symbol-macrolet define-modify-macro ' + 129 | 'most-negative-long-float symbol-name define-setf-expander ' + 130 | 'most-negative-short-float symbol-package define-setf-method ' + 131 | 'most-negative-single-float symbol-plist define-symbol-macro ' + 132 | 'most-positive-double-float symbol-value defmacro most-positive-fixnum symbolp ' + 133 | 'defmethod most-positive-long-float synonym-stream defpackage ' + 134 | 'most-positive-short-float synonym-stream-symbol defparameter ' + 135 | 'most-positive-single-float sys defsetf muffle-warning system defstruct ' + 136 | 'multiple-value-bind deftype multiple-value-call tagbody defun ' + 137 | 'multiple-value-list tailp defvar multiple-value-prog1 tan delete ' + 138 | 'multiple-value-seteq tanh delete-duplicates multiple-value-setq tenth ' + 139 | 'delete-file multiple-values-limit terpri delete-if name-char the delete-if-not ' + 140 | 'namestring third delete-package nbutlast throw denominator nconc time ' + 141 | 'deposit-field next-method-p trace describe translate-logical-pathname ' + 142 | 'describe-object nintersection translate-pathname destructuring-bind ninth ' + 143 | 'tree-equal digit-char no-applicable-method truename digit-char-p ' + 144 | 'no-next-method truncase directory not truncate directory-namestring notany ' + 145 | 'two-way-stream disassemble notevery two-way-stream-input-stream ' + 146 | 'division-by-zero notinline two-way-stream-output-stream do nreconc type do\\* ' + 147 | 'nreverse type-error do-all-symbols nset-difference type-error-datum ' + 148 | 'do-exeternal-symbols nset-exclusive-or type-error-expected-type ' + 149 | 'do-external-symbols nstring type-of do-symbols nstring-capitalize typecase ' + 150 | 'documentation nstring-downcase typep dolist nstring-upcase unbound-slot ' + 151 | 'dotimes nsublis unbound-slot-instance double-float nsubst unbound-variable ' + 152 | 'double-float-epsilon nsubst-if undefined-function ' + 153 | 'double-float-negative-epsilon nsubst-if-not unexport dpb nsubstitute unintern ' + 154 | 'dribble nsubstitute-if union dynamic-extent nsubstitute-if-not unless ecase ' + 155 | 'nth unread echo-stream nth-value unread-char echo-stream-input-stream nthcdr ' + 156 | 'unsigned-byte echo-stream-output-stream null untrace ed number unuse-package ' + 157 | 'eighth numberp unwind-protect elt numerator ' + 158 | 'update-instance-for-different-class encode-universal-time nunion ' + 159 | 'update-instance-for-redefined-class end-of-file oddp ' + 160 | 'upgraded-array-element-type endp open upgraded-complex-part-type ' + 161 | 'enough-namestring open-stream-p upper-case-p ensure-directories-exist optimize ' + 162 | 'use-package ensure-generic-function or use-value eq otherwise user eql ' + 163 | 'output-stream-p user-homedir-pathname equal package values equalp ' + 164 | 'package-error values-list error package-error-package vector etypecase ' + 165 | 'package-name vector-pop eval package-nicknames vector-push eval-when ' + 166 | 'package-shadowing-symbols vector-push-extend evalhook package-use-list vectorp ' + 167 | 'evenp package-used-by-list warn every packagep warning exp pairlis when export ' + 168 | 'parse-error wild-pathname-p expt parse-integer with-accessors extended-char ' + 169 | 'parse-namestring with-compilation-unit fboundp pathname ' + 170 | 'with-condition-restarts fceiling pathname-device with-hash-table-iterator ' + 171 | 'fdefinition pathname-directory with-input-from-string ffloor pathname-host ' + 172 | 'with-open-file fifth pathname-match-p with-open-stream file-author ' + 173 | 'pathname-name with-output-to-string file-error pathname-type ' + 174 | 'with-package-iterator file-error-pathname pathname-version with-simple-restart ' + 175 | 'file-length pathnamep with-slots file-namestring peek-char ' + 176 | 'with-standard-io-syntax file-position phase write file-stream write-byte ' + 177 | 'file-string-length plusp write-char file-write-date pop write-line fill ' + 178 | 'position write-sequence fill-pointer position-if write-string find ' + 179 | 'position-if-not write-to-string find-all-symbols pprint y-or-n-p find-class ' + 180 | 'pprint-dispatch yes-or-no-p find-if pprint-exit-if-list-exhausted zerop ' + 181 | 'find-if-not pprint-fill'; 182 | 183 | // common lisp global variables. also from lisp.vim 184 | var standard_vars = 185 | '\\*applyhook\\* \\*load-pathname\\* \\*print-pprint-dispatch\\* \\*break-on-signals\\* ' + 186 | '\\*load-print\\* \\*print-pprint-dispatch\\* \\*break-on-signals\\* \\*load-truename\\* ' + 187 | '\\*print-pretty\\* \\*break-on-warnings\\* \\*load-verbose\\* \\*print-radix\\* ' + 188 | '\\*compile-file-pathname\\* \\*macroexpand-hook\\* \\*print-readably\\* ' + 189 | '\\*compile-file-pathname\\* \\*modules\\* \\*print-right-margin\\* \\*compile-file-truename\\* ' + 190 | '\\*package\\* \\*print-right-margin\\* \\*compile-file-truename\\* \\*print-array\\* ' + 191 | '\\*query-io\\* \\*compile-print\\* \\*print-base\\* \\*random-state\\* \\*compile-verbose\\* ' + 192 | '\\*print-case\\* \\*read-base\\* \\*compile-verbose\\* \\*print-circle\\* ' + 193 | '\\*read-default-float-format\\* \\*debug-io\\* \\*print-escape\\* \\*read-eval\\* ' + 194 | '\\*debugger-hook\\* \\*print-gensym\\* \\*read-suppress\\* \\*default-pathname-defaults\\* ' + 195 | '\\*print-length\\* \\*readtable\\* \\*error-output\\* \\*print-level\\* \\*standard-input\\* ' + 196 | '\\*evalhook\\* \\*print-lines\\* \\*standard-output\\* \\*features\\* \\*print-miser-width\\* ' + 197 | '\\*terminal-io\\* \\*gensym-counter\\* \\*print-miser-width\\* \\*trace-output\\* ' + 198 | 'pi internal-time-units-per-second'; 199 | 200 | // common lisp known keywords 201 | var keywords = 202 | ':abort :from-end :overwrite :adjustable :gensym :predicate :append :host ' + 203 | ':preserve-whitespace :array :if-does-not-exist :pretty :base :if-exists :print ' + 204 | ':case :include :print-function :circle :index :probe :conc-name :inherited ' + 205 | ':radix :constructor :initial-contents :read-only :copier :initial-element ' + 206 | ':rehash-size :count :initial-offset :rehash-threshold :create :initial-value ' + 207 | ':rename :default :input :rename-and-delete :defaults :internal :size :device ' + 208 | ':io :start :direction :junk-allowed :start1 :directory :key :start2 ' + 209 | ':displaced-index-offset :length :stream :displaced-to :level :supersede ' + 210 | ':element-type :name :test :end :named :test-not :end1 :new-version :type :end2 ' + 211 | ':nicknames :use :error :output :verbose :escape :output-file :version ' + 212 | ':external :documentation :shadowing-import-from :modern :export ' + 213 | ':case-sensitive :case-inverted :shadow :import-from :intern :fill-pointer ' + 214 | ':upcase :downcase :preserve :invert :load-toplevel :compile-toplevel :execute ' + 215 | ':while :until :for :do :if :then :else :when :unless :in :across :finally ' + 216 | ':collect :nconc :maximize :minimize :sum :and :with :initially :append :into ' + 217 | ':count :end :repeat :always :never :thereis :from :to :upto :downto :below ' + 218 | ':above :by :on :being :each :the :hash-key :hash-keys :hash-value :hash-values ' + 219 | ':using :of-type :upfrom :downfrom :arguments :return-type :library :full ' + 220 | ':malloc-free :none :alloca :in :out :in-out :stdc-stdcall :stdc :c :language ' + 221 | ':built-in :typedef :external :fini :init-once :init-always'; 222 | 223 | var lambda = '&allow-other-keys &aux &body &environment &key &optional &rest &whole'; 224 | 225 | var special = 'let let\\* lambda'; 226 | 227 | /** 228 | * Given a list of items in a string: 'item1 item2 item2 ...' 229 | * 230 | * return a regex *string*: '(item1|item2|item2|...)' 231 | */ 232 | var list_to_regex = function(list) 233 | { 234 | var items = list.replace(/(^ | $)/gm, '').split(/ /g); 235 | return '('+items.join('|')+')'; 236 | }; 237 | 238 | var is_in_list = function(item, list) 239 | { 240 | var items = list.replace(/(^ | $)/gm, '').split(/ /g); 241 | for(var i = 0, n = items.length; i < n; i++) 242 | { 243 | if(items[i] == item) return true; 244 | } 245 | return false; 246 | }; 247 | 248 | /** 249 | * Collections of search and replaces to make. 250 | */ 251 | var replace = [ 252 | // --------------------------------------------------------------------- 253 | // strings (should !!ALWAYS!! be first, lest our tags be destroyed...) 254 | // --------------------------------------------------------------------- 255 | {regex: /"([\s\S]*?)"/gm, replace: '"$1"'}, 256 | 257 | // --------------------------------------------------------------------- 258 | // comments 259 | // --------------------------------------------------------------------- 260 | {regex: /(;.*)(\n|$)/gm, replace: '$1$2'}, 261 | 262 | // --------------------------------------------------------------------- 263 | // "special" (let/lambda) 264 | // --------------------------------------------------------------------- 265 | { 266 | regex: new RegExp('.'+list_to_regex(special)+'(?=[\\s()])', 'g'), 267 | replace: function(fullmatch, fnname) { 268 | if(fullmatch[0] == '(') 269 | { 270 | return '(' + fnname + ''; 271 | } 272 | else 273 | { 274 | return fullmatch; 275 | } 276 | } 277 | }, 278 | 279 | 280 | // --------------------------------------------------------------------- 281 | // function matches 282 | // --------------------------------------------------------------------- 283 | // known functions 284 | { 285 | regex: new RegExp('.'+list_to_regex(funcs)+'(?=[\\s()])', 'g'), 286 | replace: function(fullmatch, fnname) { 287 | if(fullmatch[0] == '(') 288 | { 289 | return '(' + fnname + ''; 290 | } 291 | else 292 | { 293 | return fullmatch; 294 | } 295 | } 296 | }, 297 | // symbol functions (#'my-fn) 298 | { 299 | regex: /([\s()])(#'(\w[\w_-]*))(?=[\s()])/g, 300 | replace: function(fullmatch, delim1, symfun, sym) 301 | { 302 | var known = false; 303 | if(is_in_list(sym, funcs)) 304 | { 305 | known = true; 306 | } 307 | return delim1 +''+ symfun +''; 308 | } 309 | }, 310 | 311 | // --------------------------------------------------------------------- 312 | // lambda keywords 313 | // --------------------------------------------------------------------- 314 | {regex: new RegExp('([\\s()])'+list_to_regex(lambda)+'(?=[\\s()])', 'g'), replace: '$1$2'}, 315 | 316 | // --------------------------------------------------------------------- 317 | // symbols/keywords/variables 318 | // --------------------------------------------------------------------- 319 | // generic symbols 320 | {regex: /([\s()])('\w[\w_-]*)(?=[\s()])/g, replace: '$1$2'}, 321 | // known keywords 322 | { 323 | regex: new RegExp('([\\s()])'+list_to_regex(keywords)+'([\\s()])', 'g'), 324 | replace: function(fullmatch, whitespace, keyword, whitespace2) { 325 | return whitespace + ''+ keyword +''+ whitespace2; 326 | } 327 | }, 328 | // generic keywords 329 | { 330 | regex: /([\s()])(:\w[\w_-]*)/g, 331 | replace: function(fullmatch, delim, keyword) { 332 | if(fullmatch[0].match(/[\s()]/gm)) 333 | { 334 | return delim + ''+ keyword +''; 335 | } 336 | return fullmatch; 337 | } 338 | }, 339 | // known variables 340 | { 341 | regex: new RegExp('([\\s()])'+list_to_regex(standard_vars)+'([\\s()])', 'g'), 342 | replace: function(fullmatch, whitespace, varname, whitespace2) { 343 | return whitespace + ''+ varname +''+ whitespace2; 344 | } 345 | }, 346 | // globals/constants 347 | {regex: /([\s()])(\*\w[\w_-]*\*)(?=[\s()])/g, replace: '$1$2'}, 348 | {regex: /([\s()])(\+\w[\w_-]*\+)(?=[\s()])/g, replace: '$1$2'}, 349 | 350 | // --------------------------------------------------------------------- 351 | // numbers 352 | // --------------------------------------------------------------------- 353 | // binary 354 | {regex: /([\s()])(#b[01]+)(?=[\s()])/ig, replace: '$1$2'}, 355 | // hex 356 | {regex: /([\s()])(#x[\da-f]+)(?=[\s()])/ig, replace: '$1$2'}, 357 | // float 358 | {regex: /([\s()])([+-]?(?:\d+\.\d+|\d+\.|\.\d+))(?=[\s()])/g, replace: '$1$2'}, 359 | // ratio 360 | {regex: /([\s()])([+-]?\d+(?:\/\d+)?)(?=[\s()])/g, replace: '$1$2'}, 361 | // integers 362 | {regex: /([\s()])([+-]?\d+)(?=[\s()])/g, replace: '$1$2'}, 363 | 364 | // --------------------------------------------------------------------- 365 | // misc parsers 366 | // --------------------------------------------------------------------- 367 | // t/nil 368 | {regex: /([\s()])(nil|t)(?=[\s()])/g, replace: '$1$2'}, 369 | 370 | // generic "maybe a function" forms. best second to last 371 | {regex: /\((\w[\w_:-]*)(?=[\s()])/g, replace: '($1'}, 372 | 373 | // ()'s (should most probably be last, unless there's a good reason) 374 | {regex: /([()])/g, replace: '$1'} 375 | ]; 376 | 377 | /** 378 | * Main highlight function. 379 | */ 380 | this.highlight_element = function(code_el) 381 | { 382 | code_el.className += ' hl-highlighted'; 383 | var html = code_el.innerHTML; 384 | // can't have &...;'s running wild like a pack of animals... 385 | html = html.replace(/&/g, '&'); 386 | html = html.replace(/</g, '<'); 387 | html = html.replace(/>/g, '>'); 388 | // pad the HTML string (makes regexs much simpler) 389 | html = "\n" + html + "\n"; 390 | for(var i = 0, n = replace.length; i < n; i++) 391 | { 392 | var rep = replace[i]; 393 | html = html.replace(rep.regex, rep.replace); 394 | } 395 | // unpad HTML string 396 | html = html.replace(/(^\n|\n$)/g, ''); 397 | html = html.replace(/<(?!\/?span)/g, '<'); 398 | // Re-encode stray &s to conform with XHTML 399 | //html = html.replace(/&/g, '&'); 400 | 401 | code_el.innerHTML = html; 402 | }, 403 | 404 | /** 405 | * Automatically highlight all blocks 406 | * 407 | * Takes an options arg, which can be used to specify the classname of the 408 | * tags you wish to highlight. 409 | */ 410 | this.highlight_auto = function(options) 411 | { 412 | options || (options = {}); 413 | var classname = options.className ? options.className : 'lisp'; 414 | var codes = document.getElementsByTagName('code'); 415 | for(var i = 0, n = codes.length; i < n; i++) 416 | { 417 | var code = codes[i]; 418 | if(code.className.match(classname)) 419 | { 420 | this.highlight_element(code); 421 | } 422 | } 423 | } 424 | }; 425 | 426 | var HighlightLisp = new highlight_lisp(); 427 | -------------------------------------------------------------------------------- /templates/static/highlight-lisp/themes/dark.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Inspired by highlight.js's "Tomorrow night" theme 3 | */ 4 | pre { white-space: pre; background-color: #1d1f21; border: 0px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } 5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;} 6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;} 7 | 8 | code.hl-highlighted {color: #c5c8c6;} 9 | code.hl-highlighted .function {color: #abc; color: #cba;} 10 | code.hl-highlighted .function.known {color: #81a2be; color: #91b2ce;} 11 | code.hl-highlighted .function.known.special {color: #ba7; font-weight: bold;} 12 | code.hl-highlighted .keyword {color: #b299ab;} 13 | code.hl-highlighted .keyword.known {color: #b294bb;} 14 | code.hl-highlighted .symbol {color: #c9c;} 15 | code.hl-highlighted .lambda-list {color: #9aa;} 16 | code.hl-highlighted .number {color: #c76;} 17 | code.hl-highlighted .variable.known {color: #9dd;} 18 | code.hl-highlighted .variable.global {color: #aaa;} 19 | code.hl-highlighted .variable.constant {color: #a77;} 20 | code.hl-highlighted .nil {color: #de935f;} 21 | code.hl-highlighted .list {} 22 | 23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #b5bd88 !important;} 24 | code.hl-highlighted .comment, 25 | code.hl-highlighted .comment *, 26 | code.hl-highlighted .comment .string 27 | code.hl-highlighted .comment .string * {color: #777 !important;} 28 | code.hl-highlighted .string .comment {color: #b5bd88 !important;} 29 | -------------------------------------------------------------------------------- /templates/static/highlight-lisp/themes/github.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Inspired by github's default code highlighting 3 | */ 4 | pre { white-space: pre; background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } 5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;} 6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;} 7 | 8 | code.hl-highlighted {color: #008080;} 9 | code.hl-highlighted .function {color: #008080;} 10 | code.hl-highlighted .function.known {color: #800603;} 11 | code.hl-highlighted .function.known.special {color: #2d2d2d; font-weight: bold;} 12 | code.hl-highlighted .keyword {color: #990073;} 13 | code.hl-highlighted .keyword.known {color: #990073;} 14 | code.hl-highlighted .symbol {color: #75a;} 15 | code.hl-highlighted .lambda-list {color: #966;} 16 | code.hl-highlighted .number {color: #800;} 17 | code.hl-highlighted .variable.known {color: #c3c;} 18 | code.hl-highlighted .variable.global {color: #939;} 19 | code.hl-highlighted .variable.constant {color: #229;} 20 | code.hl-highlighted .nil {color: #f00;} 21 | code.hl-highlighted .list {color: #222;} 22 | 23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #d14 !important;} 24 | code.hl-highlighted .comment, 25 | code.hl-highlighted .comment *, 26 | code.hl-highlighted .comment .string 27 | code.hl-highlighted .comment .string * {color: #aaa !important;} 28 | code.hl-highlighted .string .comment {color: #d14 !important;} 29 | -------------------------------------------------------------------------------- /templates/static/highlight-lisp/themes/vestigial.css: -------------------------------------------------------------------------------- 1 | pre { white-space: pre; background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } 2 | pre code {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;} 3 | code {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; background: #f8f8f8; border: 1px solid #eaeaea; border-radius: 3px;} 4 | 5 | code.hl-highlighted .function.known {color: #a93;} 6 | code.hl-highlighted .function.known.special {color: #2a3;} 7 | code.hl-highlighted .keyword {color: #7aa;} 8 | code.hl-highlighted .keyword.known {color: #4bb;} 9 | code.hl-highlighted .symbol {color: #75a;} 10 | code.hl-highlighted .lambda-list {color: #966;} 11 | code.hl-highlighted .number {color: #800;} 12 | code.hl-highlighted .variable.known {color: #c3c;} 13 | code.hl-highlighted .variable.global {color: #939;} 14 | code.hl-highlighted .variable.constant {color: #229;} 15 | code.hl-highlighted .nil {color: #f00;} 16 | 17 | code.hl-highlighted .comment, code.hl-highlighted .comment *, code.hl-highlighted .comment .string {color: #aaa !important;} 18 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #088 !important;} 19 | -------------------------------------------------------------------------------- /templates/static/highlight-lisp/themes/wookie.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Inspired by highlight.js's "Tomorrow night" theme 3 | */ 4 | pre { white-space: pre; background-color: #1d1f21; border: 0px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } 5 | pre code.hl-highlighted {white-space: pre; margin: 0; padding: 0; background: none; border: none; overflow-x: auto; font-size: 13px;} 6 | code.hl-highlighted {margin: 0 2px; padding: 0 5px; white-space: nowrap; font-family: Consolas, "Liberation Mono", Courier, monospace; border: 1px solid #eaeaea; border-radius: 3px;} 7 | 8 | code.hl-highlighted {color: #c5c8c6; color: #c9bf9f;} 9 | code.hl-highlighted .function {color: #d9c382;} 10 | code.hl-highlighted .function.known {color: #d9c382;} 11 | code.hl-highlighted .function.known.special {color: #d9c382; font-weight: bold;} 12 | code.hl-highlighted .keyword {color: #74b5ad;} 13 | code.hl-highlighted .keyword.known {color: #91b2ce;} 14 | code.hl-highlighted .symbol {color: #db919e;} 15 | code.hl-highlighted .lambda-list {color: #86adad;} 16 | code.hl-highlighted .number {color: #fa879c;} 17 | code.hl-highlighted .variable.known {color: #db697e;} 18 | code.hl-highlighted .variable.global {color: #96a646;} 19 | code.hl-highlighted .variable.constant {color: #96a646;} 20 | code.hl-highlighted .nil {color: #db919e; font-weight: bold;} 21 | code.hl-highlighted .list {} 22 | 23 | code.hl-highlighted .string, code.hl-highlighted .string * {color: #db7d8e !important;} 24 | code.hl-highlighted .comment, 25 | code.hl-highlighted .comment *, 26 | code.hl-highlighted .comment .string 27 | code.hl-highlighted .comment .string * {color: #777 !important;} 28 | code.hl-highlighted .string .comment {color: #db7d8e !important;} 29 | -------------------------------------------------------------------------------- /templates/static/mathjax/load-mathjax.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | chtml: { 3 | fontURL: 'static/' 4 | }, 5 | }; 6 | 7 | (function () { 8 | var script = document.createElement('script'); 9 | script.src = 'static/tex-chtml.js'; 10 | script.async = true; 11 | document.head.appendChild(script); 12 | })(); 13 | -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_AMS-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_AMS-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Calligraphic-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Calligraphic-Bold.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Calligraphic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Calligraphic-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Fraktur-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Fraktur-Bold.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Fraktur-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Fraktur-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Main-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Main-Bold.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Main-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Main-Italic.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Main-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Main-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Math-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Math-BoldItalic.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Math-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Math-Italic.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Math-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Math-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_SansSerif-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_SansSerif-Bold.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_SansSerif-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_SansSerif-Italic.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_SansSerif-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_SansSerif-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Script-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Script-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Size1-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Size1-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Size2-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Size2-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Size3-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Size3-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Size4-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Size4-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Typewriter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Typewriter-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Vector-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Vector-Bold.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Vector-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Vector-Regular.woff -------------------------------------------------------------------------------- /templates/static/mathjax/woff-v2/MathJax_Zero.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CommonDoc/codex/7536259defea1fe0f15b3bd8b7c5a19ff88d1f81/templates/static/mathjax/woff-v2/MathJax_Zero.woff -------------------------------------------------------------------------------- /templates/static/nodes.css: -------------------------------------------------------------------------------- 1 | /* Common style for documentation nodes */ 2 | 3 | /* Layout */ 4 | 5 | .codex-doc-node > * { 6 | margin-top: 20px; 7 | } 8 | 9 | .codex-slot > dd > * { 10 | margin-bottom: 10px; 11 | } 12 | 13 | .codex-lambda-list, .codex-type-def { 14 | display: inline-block; 15 | margin-left: 15px; 16 | margin-top: 0; 17 | font-size: 15px; 18 | } 19 | 20 | /* Style */ 21 | 22 | .codex-doc-node { 23 | border-left: 5px solid; 24 | display: block; 25 | padding: 0 15px 15px 15px; 26 | } 27 | 28 | .codex-name { 29 | font-weight: bold; 30 | font-size: 16px; 31 | } 32 | 33 | 34 | /* Class and struct option and slot option table styling */ 35 | 36 | .codex-class-struct-slot-option-node { 37 | display: block; 38 | margin: 15px; 39 | align-items: flex-start; 40 | padding: 0px 15px 15px 15px; 41 | } 42 | 43 | .codex-class-struct-slot-option-table { 44 | font-weight: normal; 45 | font-style: normal; 46 | font-size: 16px; 47 | text-align: left; 48 | margin-left: 15px; 49 | padding: 5px 5px 5px 5px; 50 | } 51 | 52 | .codex-class-struct-slot-option-row { 53 | } 54 | 55 | .codex-class-struct-slot-option-label-cell { 56 | font-style: italic; 57 | } 58 | 59 | .codex-class-struct-slot-option-value-cell { 60 | } 61 | 62 | .codex-class-struct-slot-option-symbol-list-cell { 63 | } 64 | 65 | /* Documentation node colors */ 66 | 67 | .codex-function { 68 | border-left-color: rgba(77, 119, 203, 0.7); 69 | } 70 | 71 | .codex-macro { 72 | border-left-color: rgba(153, 51, 204, 0.7);; 73 | } 74 | 75 | .codex-generic-function { 76 | border-left-color: rgba(46, 83, 157, 0.7); 77 | } 78 | 79 | .codex-method { 80 | border-left-color: rgba(46, 83, 157, 0.7); 81 | } 82 | 83 | .codex-class { 84 | border-left-color: rgba(224, 153, 82, 0.7); 85 | } 86 | -------------------------------------------------------------------------------- /templates/static/reset.css: -------------------------------------------------------------------------------- 1 | *, *:after, *:before { 2 | margin: 0; 3 | padding: 0; 4 | -webkit-box-sizing: border-box; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | } 8 | -------------------------------------------------------------------------------- /templates/templates.lisp: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | (defpackage codex.tmpl 3 | (:use :cl :trivial-types) 4 | (:import-from :common-doc 5 | :document 6 | :section 7 | :title) 8 | (:import-from :common-html.template 9 | :template 10 | :render 11 | :render-section) 12 | (:export :codex-template 13 | :built-in-template 14 | :*template-database* 15 | :find-template) 16 | (:documentation "Codex template definitions.")) 17 | (in-package :codex.tmpl) 18 | 19 | (defparameter +templates-directory+ 20 | (asdf:system-relative-pathname :codex #p"templates/")) 21 | 22 | ;;; Classes 23 | 24 | (defclass codex-template (template) 25 | ((output-directory :reader output-directory 26 | :initarg :output-directory 27 | :type pathname 28 | :documentation "The directory where the output will be produced.") 29 | (options :reader options 30 | :initarg :options 31 | :type property-list 32 | :documentation "A plist of template options.")) 33 | (:documentation "Codex templates.")) 34 | 35 | (defclass built-in-template (codex-template) 36 | () 37 | (:documentation "A convenience subclass for Codex built-in templates. These 38 | slots are not built into the default codex-template class, since users might 39 | define their own templates without using this machinery.")) 40 | 41 | (defgeneric document-template (template)) 42 | 43 | (defgeneric section-template (template)) 44 | 45 | (defgeneric static-files (template)) 46 | 47 | ;;; Methods 48 | 49 | (defun static-output-directory (built-in-template) 50 | (merge-pathnames #p"static/" 51 | (output-directory built-in-template))) 52 | 53 | (defmethod initialize-instance :after ((tmpl codex-template) &key) 54 | "Copy the template's static files to the output directory" 55 | (ensure-directories-exist (static-output-directory tmpl)) 56 | (loop for (input . output) in (static-files tmpl) do 57 | (let ((input-pathname (merge-pathnames input 58 | +templates-directory+)) 59 | (output-pathname (merge-pathnames output 60 | (static-output-directory tmpl)))) 61 | ;; cl-fad:copy-file does almost the same thing, but cannot append. 62 | ;; This can be solved in SBCL with restarts, but we want this 63 | ;; code to be portable, don't we? 64 | (with-open-file (input-stream input-pathname 65 | :direction :input 66 | :element-type '(unsigned-byte 8)) 67 | (with-open-file (output-stream output-pathname 68 | :direction :output 69 | :element-type '(unsigned-byte 8) 70 | ;; Important: The following allows us to 71 | ;; concatenate static files 72 | :if-exists :append 73 | :if-does-not-exist :create) 74 | (uiop:copy-stream-to-stream 75 | input-stream output-stream 76 | :element-type '(unsigned-byte 8))))))) 77 | 78 | (defmethod render ((tmpl built-in-template) (document document) content-string) 79 | "Render a built-in document template." 80 | (let ((template (document-template tmpl)) 81 | (document-title (title document))) 82 | (djula:render-template* template 83 | nil 84 | :document-title document-title 85 | :content content-string))) 86 | 87 | (defmethod render-section ((tmpl built-in-template) (document document) (section section) 88 | content-string) 89 | "Render a built-in section template." 90 | (let ((template (section-template tmpl)) 91 | (document-title (title document)) 92 | (section-title (common-doc.ops:collect-all-text (title section))) 93 | (section-ref (common-doc:reference section)) 94 | (toc (common-html.toc:multi-file-toc document))) 95 | (djula:render-template* template 96 | nil 97 | :document-title document-title 98 | :section-title section-title 99 | :section-ref section-ref 100 | :toc toc 101 | :content content-string))) 102 | 103 | ;;; Built-in templates 104 | 105 | (defmacro define-built-in-template (name &key document-template section-template 106 | static-files 107 | documentation) 108 | `(progn 109 | (defclass ,name (built-in-template) 110 | () 111 | (:documentation ,documentation)) 112 | 113 | (defmethod document-template ((template ,name)) 114 | ,document-template) 115 | 116 | (defmethod section-template ((template ,name)) 117 | ,section-template) 118 | 119 | (defmethod static-files ((template ,name)) 120 | ,static-files))) 121 | 122 | (defun append-mathjax-fonts (&rest files) 123 | (append 124 | (mapcar 125 | (lambda (pathname) 126 | (cons 127 | (uiop:enough-pathname pathname 128 | (asdf:system-relative-pathname 129 | :codex "templates/")) 130 | (make-pathname :name (pathname-name pathname) 131 | :type (pathname-type pathname)))) 132 | (cl-fad:list-directory 133 | (asdf:system-relative-pathname 134 | :codex "templates/static/mathjax/woff-v2/"))) 135 | files)) 136 | 137 | (defun template-pathname (pathname) 138 | "Get a pathname of a file in templates directory." 139 | (merge-pathnames (pathname pathname) +templates-directory+)) 140 | 141 | (define-built-in-template minima 142 | :document-template (djula:compile-template* 143 | (template-pathname #p"minima/document.html")) 144 | :section-template (djula:compile-template* 145 | (template-pathname #p"minima/section.html")) 146 | :static-files (append-mathjax-fonts 147 | (cons #p"minima/style.css" 148 | #p"style.css") 149 | (cons #p"static/reset.css" 150 | #p"style.css") 151 | (cons #p"static/nodes.css" 152 | #p"style.css") 153 | (cons #p"static/highlight-lisp/highlight-lisp.js" 154 | #p"highlight.js") 155 | (cons #p"static/highlight-lisp/themes/github.css" 156 | #p"highlight.css") 157 | (cons #p"static/mathjax/tex-chtml.js" 158 | #p"tex-chtml.js") 159 | (cons #p"static/mathjax/load-mathjax.js" 160 | #p"load-mathjax.js")) 161 | :documentation "Minimalist template.") 162 | 163 | (define-built-in-template gamma 164 | :document-template (djula:compile-template* 165 | (template-pathname #p"gamma/document.html")) 166 | :section-template (djula:compile-template* 167 | (template-pathname #p"gamma/section.html")) 168 | :static-files (append-mathjax-fonts 169 | (cons #p"gamma/style.css" 170 | #p"style.css") 171 | (cons #p"static/reset.css" 172 | #p"style.css") 173 | (cons #p"static/nodes.css" 174 | #p"style.css") 175 | (cons #p"static/highlight-lisp/highlight-lisp.js" 176 | #p"highlight.js") 177 | (cons #p"static/highlight-lisp/themes/github.css" 178 | #p"highlight.css") 179 | (cons #p"static/mathjax/tex-chtml.js" 180 | #p"tex-chtml.js") 181 | (cons #p"static/mathjax/load-mathjax.js" 182 | #p"load-mathjax.js")) 183 | :documentation "Modern template.") 184 | 185 | ;;; Template database 186 | 187 | (defvar *template-database* 188 | (let ((table (make-hash-table))) 189 | (setf (gethash :minima table) 190 | (find-class 'minima)) 191 | (setf (gethash :gamma table) 192 | (find-class 'gamma)) 193 | table) 194 | "A hash table of table names (Keywords) to template classes.") 195 | 196 | (defun find-template (template-name) 197 | "Find a template by name." 198 | (gethash template-name *template-database*)) 199 | --------------------------------------------------------------------------------