aboutsummaryrefslogtreecommitdiff
path: root/modules/gen-html-docs
diff options
context:
space:
mode:
authorStuart Sierra <mail@stuartsierra.com>2010-08-07 16:41:53 -0400
committerStuart Sierra <mail@stuartsierra.com>2010-08-07 16:41:53 -0400
commita6a92b9b3d2bfd9a56e1e5e9cfba706d1aeeaae5 (patch)
treef1f3da9887dc2dc557df3282b0bcbd4d701ec593 /modules/gen-html-docs
parente7930c85290f77815cdb00a60604feedfa2d0194 (diff)
Split all namespaces into sub-modules.
* Examples and tests have not been copied over. * Clojure test/compile phases are commented out in parent POM. * May require installing parent POM before full build.
Diffstat (limited to 'modules/gen-html-docs')
-rw-r--r--modules/gen-html-docs/pom.xml26
-rw-r--r--modules/gen-html-docs/src/main/clojure/clojure/contrib/gen_html_docs.clj540
2 files changed, 566 insertions, 0 deletions
diff --git a/modules/gen-html-docs/pom.xml b/modules/gen-html-docs/pom.xml
new file mode 100644
index 00000000..587c7e60
--- /dev/null
+++ b/modules/gen-html-docs/pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http//www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.clojure.contrib</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../parent</relativePath>
+ </parent>
+ <artifactId>gen-html-docs</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.clojure.contrib</groupId>
+ <artifactId>io</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.clojure.contrib</groupId>
+ <artifactId>string</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/modules/gen-html-docs/src/main/clojure/clojure/contrib/gen_html_docs.clj b/modules/gen-html-docs/src/main/clojure/clojure/contrib/gen_html_docs.clj
new file mode 100644
index 00000000..73166510
--- /dev/null
+++ b/modules/gen-html-docs/src/main/clojure/clojure/contrib/gen_html_docs.clj
@@ -0,0 +1,540 @@
+;;; gen-html-docs.clj: Generate HTML documentation for Clojure libs
+
+;; by Craig Andera, http://pluralsight.com/craig, candera@wangdera.com
+;; February 13th, 2009
+
+;; Copyright (c) Craig Andera, 2009. All rights reserved. The use
+;; and distribution terms for this software are covered by the Eclipse
+;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;; which can be found in the file epl-v10.html at the root of this
+;; distribution. By using this software in any fashion, you are
+;; agreeing to be bound by the terms of this license. You must not
+;; remove this notice, or any other, from this software.
+
+;; Generates a single HTML page that contains the documentation for
+;; one or more Clojure libraries. See the comments section at the end
+;; of this file for usage.
+
+;; TODO
+;;
+;; * Make symbols in the source hyperlinks to the appropriate section
+;; of the documentation.
+;; * Investigate issue with miglayout mentioned here:
+;; http://groups.google.com/group/clojure/browse_thread/thread/5a0c4395e44f5a79/3ae483100366bd3d?lnk=gst&q=documentation+browser#3ae483100366bd3d
+;;
+;; DONE
+;;
+;; * Move to clojure.contrib
+;; * Change namespace
+;; * Change license as appropriate
+;; * Double-check doc strings
+;; * Remove doc strings from source code
+;; * Add collapse/expand functionality for all namespaces
+;; * Add collapse/expand functionality for each namespace
+;; * See if converting to use clojure.contrib.prxml is possible
+;; * Figure out why the source doesn't show up for most things
+;; * Add collapsible source
+;; * Add links at the top to jump to each namespace
+;; * Add object type (var, function, whatever)
+;; * Add argument lists for functions
+;; * Add links at the top of each namespace to jump to members
+;; * Add license statement
+;; * Remove the whojure dependency
+
+(ns
+ ^{:author "Craig Andera",
+ :doc "Generates a single HTML page that contains the documentation for
+one or more Clojure libraries."}
+ clojure.contrib.gen-html-docs
+ (:require [clojure.contrib.io :as io]
+ [clojure.contrib.string :as s])
+ (:use [clojure.contrib repl-utils def prxml])
+ (:import [java.lang Exception]
+ [java.util.regex Pattern]))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Doc generation constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(def *script* " // <![CDATA[
+
+function getElem(id)
+{
+ if( document.getElementById )
+ {
+ return document.getElementById( id )
+ }
+ else if ( document.all )
+ {
+ return eval( 'document.all.' + id )
+ }
+ else
+ return false;
+}
+
+function setDisplayStyle(id,displayStyle)
+{
+ var elem = getElem (id)
+ if (elem)
+ {
+ elem.style.display = displayStyle
+ }
+
+}
+
+function setLinkToggleText (id, text)
+{
+ var elem = getElem (id)
+ if (elem)
+ {
+ elem.innerHTML = text
+ }
+}
+
+function collapse(id)
+{
+ setDisplayStyle (id, 'none')
+}
+
+function expand (id)
+{
+ setDisplayStyle (id, 'block')
+}
+
+function toggleSource( id )
+{
+ toggle(id, 'linkto-' + id, 'Hide Source', 'Show Source')
+}
+
+function toggle(targetid, linkid, textWhenOpen, textWhenClosed)
+{
+ var elem = getElem (targetid)
+ var link = getElem (linkid)
+
+ if (elem && link)
+ {
+ var isOpen = false
+ if (elem.style.display == '')
+ {
+ isOpen = link.innerHTML == textWhenOpen
+ }
+ else if( elem.style.display == 'block' )
+ {
+ isOpen = true
+ }
+
+ if (isOpen)
+ {
+ elem.style.display = 'none'
+ link.innerHTML = textWhenClosed
+ }
+ else
+ {
+ elem.style.display = 'block'
+ link.innerHTML = textWhenOpen
+ }
+ }
+}
+
+ //]]>
+")
+
+(def *style* "
+.library
+{
+ padding: 0.5em 0 0 0
+}
+.all-libs-toggle,.library-contents-toggle
+{
+ font-size: small;
+}
+.all-libs-toggle a,.library-contents-toggle a
+{
+ color: white
+}
+.library-member-doc-whitespace
+{
+ white-space: pre
+}
+.library-member-source-toggle
+{
+ font-size: small;
+ margin-top: 0.5em
+}
+.library-member-source
+{
+ display: none;
+ border-left: solid lightblue
+}
+.library-member-docs
+{
+ font-family:monospace
+}
+.library-member-arglists
+{
+ font-family: monospace
+}
+.library-member-type
+{
+ font-weight: bold;
+ font-size: small;
+ font-style: italic;
+ color: darkred
+}
+.lib-links
+{
+ margin: 0 0 1em 0
+}
+
+.lib-link-header
+{
+ color: white;
+ background: darkgreen;
+ width: 100%
+}
+
+.library-name
+{
+ color: white;
+ background: darkblue;
+ width: 100%
+}
+
+.missing-library
+{
+ color: darkred;
+ margin: 0 0 1em 0
+}
+
+.library-members
+{
+ list-style: none
+}
+
+.library-member-name
+{
+ font-weight: bold;
+ font-size: 105%
+}")
+
+(defn- extract-documentation
+ "Pulls the documentation for a var v out and turns it into HTML"
+ [v]
+ (if-let [docs (:doc (meta v))]
+ (map
+ (fn [l]
+ [:div {:class "library-member-doc-line"}
+ (if (= 0 (count l))
+ [:span {:class "library-member-doc-whitespace"} " "] ; We need something here to make the blank line show up
+ l)])
+ (s/split #"\n" docs))
+ ""))
+
+(defn- member-type
+ "Figures out for a var x whether it's a macro, function, var or multifunction"
+ [x]
+ (try
+ (let [dx (deref x)]
+ (cond
+ (:macro (meta x)) :macro
+ (fn? dx) :fn
+ (= clojure.lang.MultiFn (:tag (meta x))) :multi
+ true :var))
+ (catch Exception e
+ :unknown)))
+
+(defn- anchor-for-member
+ "Returns a suitable HTML anchor name given a library id and a member
+ id"
+ [libid memberid]
+ (str "member-" libid "-" memberid))
+
+(defn- id-for-member-source
+ "Returns a suitable HTML id for a source listing given a library and
+ a member"
+ [libid memberid]
+ (str "membersource-" libid "-" memberid))
+
+(defn- id-for-member-source-link
+ "Returns a suitable HTML id for a link to a source listing given a
+ library and a member"
+ [libid memberid]
+ (str "linkto-membersource-" libid "-" memberid))
+
+(defn- symbol-for
+ "Given a namespace object ns and a namespaceless symbol memberid
+ naming a member of that namespace, returns a namespaced symbol that
+ identifies that member."
+ [ns memberid]
+ (symbol (name (ns-name ns)) (name memberid)))
+
+(defn- elide-to-one-line
+ "Elides a string down to one line."
+ [s]
+ (s/replace-re #"(\n.*)+" "..." s))
+
+(defn- elide-string
+ "Returns a string that is at most the first limit characters of s"
+ [s limit]
+ (if (< (- limit 3) (count s))
+ (str (subs s 0 (- limit 3)) "...")
+ s))
+
+(defn- doc-elided-src
+ "Returns the src with the docs elided."
+ [docs src]
+ (s/replace-re (re-pattern (str "\"" (Pattern/quote docs) "\""))
+ (str "\""
+ (elide-to-one-line docs)
+;; (elide-string docs 10)
+;; "..."
+ "\"")
+ src))
+
+(defn- format-source [libid memberid v]
+ (try
+ (let [docs (:doc (meta v))
+ src (if-let [ns (find-ns libid)]
+ (get-source (symbol-for ns memberid)))]
+ (if (and src docs)
+ (doc-elided-src docs src)
+ src))
+ (catch Exception ex
+ nil)))
+
+(defn- generate-lib-member [libid [n v]]
+ [:li {:class "library-member"}
+ [:a {:name (anchor-for-member libid n)}]
+ [:dl {:class "library-member-table"}
+ [:dt {:class "library-member-name"}
+ (str n)]
+ [:dd
+ [:div {:class "library-member-info"}
+ [:span {:class "library-member-type"} (name (member-type v))]
+ " "
+ [:span {:class "library-member-arglists"} (str (:arglists (meta v)))]]
+ (into [:div {:class "library-member-docs"}] (extract-documentation v))
+ (let [member-source-id (id-for-member-source libid n)
+ member-source-link-id (id-for-member-source-link libid n)]
+ (if-let [member-source (format-source libid n v)]
+ [:div {:class "library-member-source-section"}
+ [:div {:class "library-member-source-toggle"}
+ "[ "
+ [:a {:href (format "javascript:toggleSource('%s')" member-source-id)
+ :id member-source-link-id} "Show Source"]
+ " ]"]
+ [:div {:class "library-member-source" :id member-source-id}
+ [:pre member-source]]]))]]])
+
+(defn- anchor-for-library
+ "Given a symbol id identifying a namespace, returns an identifier
+suitable for use as the name attribute of an HTML anchor tag."
+ [id]
+ (str "library-" id))
+
+(defn- generate-lib-member-link
+ "Emits a hyperlink to a member of a namespace given libid (a symbol
+identifying the namespace) and the vector [n v], where n is the symbol
+naming the member in question and v is the var pointing to the
+member."
+ [libid [n v]]
+ [:a {:class "lib-member-link"
+ :href (str "#" (anchor-for-member libid n))} (name n)])
+
+(defn- anchor-for-library-contents
+ "Returns an HTML ID that identifies the element that holds the
+documentation contents for the specified library."
+ [lib]
+ (str "library-contents-" lib))
+
+(defn- anchor-for-library-contents-toggle
+ "Returns an HTML ID that identifies the element that toggles the
+visibility of the library contents."
+ [lib]
+ (str "library-contents-toggle-" lib))
+
+(defn- generate-lib-doc
+ "Emits the HTML that documents the namespace identified by the
+symbol lib."
+ [lib]
+ [:div {:class "library"}
+ [:a {:name (anchor-for-library lib)}]
+ [:div {:class "library-name"}
+ [:span {:class "library-contents-toggle"}
+ "[ "
+ [:a {:id (anchor-for-library-contents-toggle lib)
+ :href (format "javascript:toggle('%s', '%s', '-', '+')"
+ (anchor-for-library-contents lib)
+ (anchor-for-library-contents-toggle lib))}
+ "-"]
+ " ] "]
+ (name lib)]
+ (let [ns (find-ns lib)]
+ (if ns
+ (let [lib-members (sort (ns-publics ns))]
+ [:a {:name (anchor-for-library lib)}]
+ [:div {:class "library-contents" :id (anchor-for-library-contents lib)}
+ (into [:div {:class "library-member-links"}]
+ (interpose " " (map #(generate-lib-member-link lib %) lib-members)))
+ (into [:ol {:class "library-members"}]
+ (map #(generate-lib-member lib %) lib-members))])
+ [:div {:class "missing-library library-contents" :id (anchor-for-library-contents lib)} "Could not load library"]))])
+
+(defn- load-lib
+ "Calls require on the library identified by lib, eating any
+exceptions."
+ [lib]
+ (try
+ (require lib)
+ (catch java.lang.Exception x
+ nil)))
+
+(defn- generate-lib-link
+ "Generates a hyperlink to the documentation for a namespace given
+lib, a symbol identifying that namespace."
+ [lib]
+ (let [ns (find-ns lib)]
+ (if ns
+ [:a {:class "lib-link" :href (str "#" (anchor-for-library lib))} (str (ns-name ns))])))
+
+(defn- generate-lib-links
+ "Generates the list of hyperlinks to each namespace, given libs, a
+vector of symbols naming namespaces."
+ [libs]
+ (into [:div {:class "lib-links"}
+ [:div {:class "lib-link-header"} "Namespaces"
+ [:span {:class "all-libs-toggle"}
+ " [ "
+ [:a {:href "javascript:expandAllNamespaces()"}
+ "Expand All"]
+ " ] [ "
+ [:a {:href "javascript:collapseAllNamespaces()"}
+ "Collapse All"]
+ " ]"]]]
+ (interpose " " (map generate-lib-link libs))))
+
+(defn generate-toggle-namespace-script
+ [action toggle-text lib]
+ (str (format "%s('%s');\n" action (anchor-for-library-contents lib))
+ (format "setLinkToggleText('%s', '%s');\n" (anchor-for-library-contents-toggle lib) toggle-text)))
+
+(defn generate-all-namespaces-action-script
+ [action toggle-text libs]
+ (str (format "function %sAllNamespaces()" action)
+ \newline
+ "{"
+ \newline
+ (reduce str (map #(generate-toggle-namespace-script action toggle-text %) libs))
+ \newline
+ "}"))
+
+(defn generate-documentation
+ "Returns a string which is the HTML documentation for the libraries
+named by libs. Libs is a vector of symbols identifying Clojure
+libraries."
+ [libs]
+ (dorun (map load-lib libs))
+ (let [writer (new java.io.StringWriter)]
+ (binding [*out* writer]
+ (prxml
+ [:html {:xmlns "http://www.w3.org/1999/xhtml"}
+ [:head
+ [:title "Clojure documentation browser"]
+ [:style *style*]
+ [:script {:language "JavaScript" :type "text/javascript"} [:raw! *script*]]
+
+ [:script {:language "JavaScript" :type "text/javascript"}
+ [:raw! "// <![CDATA[!" \newline]
+ (generate-all-namespaces-action-script "expand" "-" libs)
+ (generate-all-namespaces-action-script "collapse" "+" libs)
+ [:raw! \newline "// ]]>"]]]
+ (let [lib-vec (sort libs)]
+ (into [:body (generate-lib-links lib-vec)]
+ (map generate-lib-doc lib-vec)))]))
+ (.toString writer)))
+
+
+(defn generate-documentation-to-file
+ "Calls generate-documentation on the libraries named by libs and
+emits the generated HTML to the path named by path."
+ [path libs]
+ (io/spit path (generate-documentation libs)))
+
+(comment
+ (generate-documentation-to-file
+ "C:/TEMP/CLJ-DOCS.HTML"
+ ['clojure.contrib.accumulators])
+
+ (defn gen-all-docs []
+ (generate-documentation-to-file
+ "C:/temp/clj-libs.html"
+ [
+ 'clojure.set
+ 'clojure.main
+ 'clojure.core
+ 'clojure.zip
+ 'clojure.xml
+ 'clojure.contrib.accumulators
+ 'clojure.contrib.apply-macro
+ 'clojure.contrib.auto-agent
+ 'clojure.contrib.combinatorics
+ 'clojure.contrib.command-line
+ 'clojure.contrib.complex-numbers
+ 'clojure.contrib.cond
+ 'clojure.contrib.def
+ 'clojure.contrib.io
+ 'clojure.contrib.enum
+ 'clojure.contrib.error-kit
+ 'clojure.contrib.except
+ 'clojure.contrib.fcase
+ 'clojure.contrib.generic
+ 'clojure.contrib.generic.arithmetic
+ 'clojure.contrib.generic.collection
+ 'clojure.contrib.generic.comparison
+ 'clojure.contrib.generic.functor
+ 'clojure.contrib.generic.math-functions
+ 'clojure.contrib.import-static
+ 'clojure.contrib.javadoc
+ 'clojure.contrib.javalog
+ 'clojure.contrib.lazy-seqs
+ 'clojure.contrib.lazy-xml
+ 'clojure.contrib.macro-utils
+ 'clojure.contrib.macros
+ 'clojure.contrib.math
+ 'clojure.contrib.miglayout
+ 'clojure.contrib.mmap
+ 'clojure.contrib.monads
+ 'clojure.contrib.ns-utils
+ 'clojure.contrib.prxml
+ 'clojure.contrib.repl-ln
+ 'clojure.contrib.repl-utils
+ 'clojure.contrib.seq
+ 'clojure.contrib.server-socket
+ 'clojure.contrib.shell
+ 'clojure.contrib.sql
+ 'clojure.contrib.stream-utils
+ 'clojure.contrib.string
+ 'clojure.contrib.test-contrib
+ 'clojure.contrib.trace
+ 'clojure.contrib.types
+ 'clojure.contrib.zip-filter
+ 'clojure.contrib.javadoc.browse
+ 'clojure.contrib.json.read
+ 'clojure.contrib.json.write
+ 'clojure.contrib.lazy-xml.with-pull
+ 'clojure.contrib.miglayout.internal
+ 'clojure.contrib.probabilities.finite-distributions
+ 'clojure.contrib.probabilities.monte-carlo
+ 'clojure.contrib.probabilities.random-numbers
+ 'clojure.contrib.sql.internal
+ 'clojure.contrib.test-clojure.evaluation
+ 'clojure.contrib.test-clojure.for
+ 'clojure.contrib.test-clojure.numbers
+ 'clojure.contrib.test-clojure.printer
+ 'clojure.contrib.test-clojure.reader
+ 'clojure.contrib.test-clojure.sequences
+ 'clojure.contrib.test-contrib.shell
+ 'clojure.contrib.test-contrib.string
+ 'clojure.contrib.zip-filter.xml
+ ]))
+ )