├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── example ├── README.md ├── project.clj ├── src │ └── example │ │ └── core.clj └── test │ ├── documentation │ ├── example │ │ ├── component.clj │ │ ├── home.clj │ │ ├── quickstart.clj │ │ ├── scheduler.clj │ │ └── watch.clj │ ├── logic_tut │ │ └── index.clj │ ├── on_lisp │ │ ├── book.clj │ │ ├── ch1_the_extensible_language.clj │ │ └── ch2_functions.clj │ └── short │ │ ├── t1_global.clj │ │ ├── t2_article.clj │ │ ├── t3_namespaces.clj │ │ ├── t4_numbers.clj │ │ ├── t5_references.clj │ │ ├── t6_stencil.clj │ │ ├── t7_pluggable.clj │ │ └── t8_citation.clj │ └── example │ └── core_test.clj ├── hydrox.graffle ├── lein ├── .gitignore ├── LICENSE ├── docs │ ├── css │ │ ├── rdash.min.css │ │ └── scrollspy.css │ ├── img │ │ ├── favicon.png │ │ ├── logo-white.png │ │ └── logo.png │ ├── js │ │ └── angular-highlightjs.min.js │ └── sample-document.html ├── project.clj ├── resources │ └── hydrox │ │ ├── sample.edn │ │ ├── template │ │ ├── article.html │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── rdash.min.css │ │ │ │ └── scrollspy.css │ │ │ ├── img │ │ │ │ ├── favicon.png │ │ │ │ ├── logo-white.png │ │ │ │ └── logo.png │ │ │ └── js │ │ │ │ └── angular-highlightjs.min.js │ │ └── partials │ │ │ ├── deps-web.html │ │ │ └── navbar.html │ │ └── test │ │ └── documentation │ │ └── sample_document.clj ├── src │ └── leiningen │ │ ├── hydrox.clj │ │ └── hydrox │ │ ├── init.clj │ │ └── setup.clj ├── template │ ├── article.html │ ├── assets │ │ ├── css │ │ │ ├── rdash.min.css │ │ │ └── scrollspy.css │ │ ├── img │ │ │ ├── favicon.png │ │ │ ├── logo-white.png │ │ │ └── logo.png │ │ └── js │ │ │ └── angular-highlightjs.min.js │ └── partials │ │ ├── deps-web.html │ │ └── navbar.html └── test │ └── documentation │ └── sample_document.clj ├── project.clj ├── src └── hydrox │ ├── analyse.clj │ ├── analyse │ ├── common.clj │ ├── source.clj │ ├── test.clj │ └── test │ │ ├── clojure.clj │ │ ├── common.clj │ │ └── midje.clj │ ├── common │ ├── data.clj │ └── util.clj │ ├── core.clj │ ├── core │ ├── patch.clj │ └── regulator.clj │ ├── doc.clj │ ├── doc │ ├── checks.clj │ ├── collect.clj │ ├── link.clj │ ├── link │ │ ├── anchors.clj │ │ ├── namespaces.clj │ │ ├── numbers.clj │ │ ├── references.clj │ │ ├── stencil.clj │ │ └── tags.clj │ ├── parse.clj │ ├── render.clj │ ├── render │ │ ├── article.clj │ │ ├── navigation.clj │ │ ├── toc.clj │ │ └── util.clj │ └── structure.clj │ ├── meta.clj │ └── meta │ └── util.clj ├── template ├── article.html ├── assets │ ├── css │ │ ├── rdash.min.css │ │ └── scrollspy.css │ ├── img │ │ ├── big.png │ │ ├── favicon.png │ │ ├── hydrox-overview.png │ │ ├── hydrox.png │ │ ├── logo-white.png │ │ └── logo.png │ └── js │ │ └── angular-highlightjs.min.js └── partials │ ├── deps-web.html │ └── navbar.html └── test ├── documentation ├── hydrox_guide.clj ├── hydrox_guide │ ├── api.clj │ └── bug_example.clj └── sample_document.clj └── hydrox ├── analyse ├── source_test.clj ├── test │ ├── clojure_test.clj │ ├── common_test.clj │ └── midje_test.clj └── test_test.clj ├── analyse_test.clj ├── common ├── data_test.clj └── util_test.clj ├── core └── regulator_test.clj ├── core_test.clj ├── doc ├── checks_test.clj ├── collect_test.clj ├── link │ ├── anchors_test.clj │ ├── namespaces_test.clj │ ├── numbers_test.clj │ ├── references_test.clj │ ├── stencil_test.clj │ └── tags_test.clj ├── parse_test.clj ├── render │ └── util_test.clj ├── render_test.clj └── structure_test.clj ├── doc_test.clj ├── meta └── util_test.clj └── meta_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | *.*# 13 | .#* 14 | .DS_folio 15 | ./*.html 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs"] 2 | path = docs 3 | url = https://github.com/helpshift/hydrox.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | lein: lein2 3 | script: 4 | - lein2 midje 5 | notifications: 6 | email: 7 | recipients: 8 | - chris@helpshift.com 9 | - bg@helpshift.com 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hydrox 2 | 3 | [![Build Status](https://travis-ci.org/helpshift/hydrox.svg?branch=master)](https://travis-ci.org/helpshift/hydrox) 4 | 5 | dive deeper into your code 6 | 7 | [![hydrox logo](https://raw.githubusercontent.com/helpshift/hydrox/master/template/assets/img/big.png)](http://helpshift.github.io/hydrox) 8 | 9 | ## Installation 10 | 11 | In your `project.clj`, add hydrox to the `[:profiles :dev :dependencies]` entry: 12 | 13 | ```clojure 14 | (defproject ... 15 | ... 16 | :plugins ["lein-hydrox" "0.1.15"] 17 | :profiles {:dev {:dependencies [... 18 | [helpshift/hydrox "0.1.15"] 19 | ...]}} 20 | ...) 21 | ``` 22 | 23 | ## Leiningen 24 | 25 | To initialise a project: 26 | 27 | ```shell 28 | lein hydrox init 29 | ``` 30 | 31 | To generate documentation (watches project for changes and updates documentation accordingly): 32 | 33 | ```shell 34 | lein hydrox 35 | ``` 36 | 37 | ## Tutorials 38 | 39 | - [0 - Introduction](https://youtu.be/3MIaucjfJcE) 40 | - [1 - Getting Started](https://youtu.be/p93LmHOzy6Q) 41 | 42 | ## Overview 43 | 44 | [hydrox](https://www.github.com/helpshift/hydrox) assists in the transmission of knowledge around a clojure project, providing in-repl management of documentation, docstrings and metadata through the reuse/repurposing of test code. The tool allows for a design-orientated workflow for the programming process, blurring the boundaries between design, development, testing and documentation. 45 | 46 | ![hydrox overview](https://raw.githubusercontent.com/helpshift/hydrox/master/template/assets/img/hydrox-overview.png) 47 | 48 | Please see the [docs](http://helpshift.github.io/hydrox) for more information (generated using itself). 49 | 50 | ## Links and References: 51 | 52 | - the generated website for [hara](https://www.github.com/zcaudate/hara) can be seen [here](http://docs.caudate.me/hara) 53 | 54 | - the `.html` [output](http://helpshift.github.io/hydrox/sample-document.html) can be seen for a sample `.clj` [input](https://github.com/helpshift/hydrox/blob/master/test/documentation/sample_document.clj) 55 | 56 | ## License 57 | 58 | Copyright © 2015 [Helpshift](https://www.helpshift.com/) 59 | 60 | Distributed under the Eclipse Public License either version 1.0 or (at 61 | your option) any later version. 62 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | This is an example README 2 | -------------------------------------------------------------------------------- /example/project.clj: -------------------------------------------------------------------------------- 1 | (defproject example "0.1.0" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.6.0"] 7 | [hara "2.2.0-SNAPSHOT"]] 8 | :documentation {:type :portfolio 9 | :name "example" 10 | :output "doc" 11 | :description "code patterns and utilities" 12 | :tracking "UA-31320512-2" 13 | :owners [{:name "Chris Zheng" 14 | :email "z@caudate.me" 15 | :website "http://z.caudate.me"}] 16 | :paths ["test/documentation"] 17 | :files {"home" 18 | {:input "test/documentation/example/home.clj"} 19 | "logic" 20 | {:input "test/documentation/logic_tut/index.clj" 21 | :title "Relational and Logic Programming"} 22 | "on-lisp" 23 | {:input "test/documentation/on_lisp/book.clj"} 24 | "quickstart" 25 | {:input "test/documentation/example/quickstart.clj"} 26 | "component" 27 | {:input "test/documentation/example/component.clj"} 28 | "watch" 29 | {:input "test/documentation/example/watch.clj"} 30 | "scheduler" 31 | {:input "test/documentation/example/scheduler.clj"}} 32 | :html {:logo "hara.png" 33 | :theme "clean" 34 | :home "home" 35 | :navigation ["quickstart" 36 | "on-lisp" 37 | ["guides" ["component" 38 | "ova" 39 | "watch" 40 | "scheduler"]] 41 | {:link "api", :text "api"} 42 | {:link "https://gitter.im/zcaudate/hara", 43 | :text "support"} 44 | {:link "https://www.github.com/zcaudate/hara", 45 | :text "source"}]} 46 | :link {:auto-tag true 47 | :auto-number true}}) 48 | -------------------------------------------------------------------------------- /example/src/example/core.clj: -------------------------------------------------------------------------------- 1 | (ns example.core) 2 | 3 | (defn foo 4 | "I don't do a whole lot." 5 | [x] 6 | (println x "Hello, World!")) 7 | -------------------------------------------------------------------------------- /example/test/documentation/example/component.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.example.component 2 | (:use midje.sweet)) 3 | -------------------------------------------------------------------------------- /example/test/documentation/example/quickstart.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.example.quickstart 2 | (:use midje.sweet)) 3 | -------------------------------------------------------------------------------- /example/test/documentation/example/scheduler.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.example.scheduler 2 | (:use midje.sweet)) 3 | -------------------------------------------------------------------------------- /example/test/documentation/example/watch.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.example.watch 2 | (:use midje.sweet)) 3 | -------------------------------------------------------------------------------- /example/test/documentation/logic_tut/index.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.logic-tut.index 2 | (:use midje.sweet)) 3 | 4 | [[:chapter {:title "Introduction"}]] 5 | 6 | "This tutorial will guide you through the magic and fun of combining relational programming 7 | (also known as logic programming) with functional programming. This tutorial does not assume 8 | that you have any knowledge of Lisp, Clojure, Java, or even functional programming. The only 9 | thing this tutorial assumes is that you are not afraid of using the command line and you have 10 | used at least one programming language before in your life." 11 | 12 | [[:section {:title "Why Logic Programming?"}]] 13 | 14 | "What's the point of writing programs in the relational paradigm? 15 | 16 | First off, aesthetics dammit. 17 | 18 | Logic programs are simply beautiful as they often have a declarative nature which trumps 19 | even the gems found in functional programming languages. Logic programs use search, and thus 20 | they are often not muddied up by algorithmic details. If you haven't tried Prolog before, 21 | relational programming will at times seem almost magical. However, I admit, the most important 22 | reason to learn the relational paradigm is because it's FUN." 23 | 24 | [[:section {:title "Getting Started"}]] 25 | 26 | " 27 | 1. Install `lein` following [instructions here](http://leiningen.org/#install) 28 | - `git clone https://github.com/swannodette/logic-tutorial && cd logic-tutorial` 29 | 30 | Ok, we're ready to begin. Type `lein repl`, which will drop you into the Clojure prompt. 31 | First let's double check that everything went ok. Enter the following at the Clojure REPL: 32 | " 33 | 34 | (require 'clojure.core.logic) 35 | 36 | " 37 | The REPL should print `nil` and it should return control to you. If it doesn't file an 38 | issue for this tutorial and I'll look into it. If all goes well run the following: 39 | " 40 | 41 | (load "logic_tutorial/tut1") 42 | 43 | " 44 | You'll see some harmless warnings, then run the following: 45 | " 46 | 47 | (in-ns 'logic-tutorial.tut1) 48 | 49 | "Your prompt will change and you're now working in a place that has the magic of relational 50 | programming available to you. The REPL prompt will show `logic-tutorial.tut1`, we're going 51 | show `tut1` to keep things concise." 52 | 53 | [[:chapter {:title "Exploration"}]] 54 | 55 | "Unlike most programming systems, with relational programming we can actually ask the 56 | computer questions. But before we ask the computer questions, we need define some facts! The 57 | first thing we want the computer to know about is that there are men:" 58 | 59 | (fact 60 | (db-rel man x) 61 | => #'man) 62 | 63 | "And then we want to define some men:" 64 | 65 | (def men 66 | (db [man 'Bob] 67 | [man 'John])) 68 | 69 | [[:section {:title "Question and Answer"}]] 70 | 71 | "Now we can ask who are men. Questions are always asked with `run` or `run*`. By convention 72 | we'll declare a logic variable `q` and ask the computer to give use the possible values for 73 | `q`. Here's an example:" 74 | 75 | (fact 76 | (with-db men 77 | (run 1 [q] (man q))) 78 | => '(John)) 79 | 80 | [[:subsection {:title "Multiple Results"}]] 81 | 82 | "We're asking the computer to give us at least one answer to the question - Who is a man?. 83 | We can ask for more than one answer:" 84 | 85 | (fact 86 | (with-db men 87 | (run 2 [q] (man q))) 88 | => '(John Bob)) 89 | 90 | "Now that is pretty cool. What happens if we ask for even more answers?" 91 | 92 | (fact 93 | (with-db men 94 | (run 3 [q] (man q))) 95 | => '(John Bob)) 96 | 97 | "The same result. That's because we’ve only told the computer that two men exist in the 98 | world. It can't give results for things it doesn't know about." 99 | 100 | [[:section {:title "Fun People"}]] 101 | 102 | "Let's define another kind of relationship and a fact:" 103 | 104 | (fact 105 | (db-rel fun x) 106 | => #'fun) 107 | 108 | (def fun-people 109 | (db [fun 'Bob])) 110 | 111 | "Let's ask a new kind of question:" 112 | 113 | (fact 114 | (with-dbs [men fun-people] 115 | (run* [q] 116 | (man q) 117 | (fun q))) 118 | => '(Bob)) 119 | -------------------------------------------------------------------------------- /example/test/documentation/on_lisp/book.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.on-lisp.book 2 | (:use midje.sweet)) 3 | 4 | [[:chapter {:title "The Extensible language"}]] 5 | 6 | [[:file {:src "test/documentation/on_lisp/ch1_the_extensible_language.clj"}]] 7 | 8 | [[:chapter {:title "Functions"}]] 9 | 10 | [[:file {:src "test/documentation/on_lisp/ch2_functions.clj"}]] 11 | -------------------------------------------------------------------------------- /example/test/documentation/on_lisp/ch2_functions.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.on-lisp.ch2-functions 2 | (:use midje.sweet)) 3 | 4 | "Functions are the building-blocks of Lisp programs. They are also the building-blocks of Lisp. In most 5 | languages the + operator is something quite different from user-defined functions. But Lisp has a single model, 6 | function application, to describe all the computation done by a program. The Lisp + operator is a function, 7 | just like the ones you can define yourself. 8 | 9 | In fact, except for a small number of operators called special forms, the core of Lisp is a collection of Lisp 10 | functions. What's to stop you from adding to this collection? Nothing at all: if you think of something you 11 | wish Lisp could do, you can write it yourself, and your new function will be treated just like the built-in ones. 12 | 13 | This fact has important consequences for the programmer. It means that any new function could be considered 14 | either as an addition to Lisp, or as part of a specific application. Typically, an experienced Lisp programmer 15 | will write some of each, adjusting the boundary between language and application until the two fit one another 16 | perfectly. This book is about how to achieve a good fit between language and application. Since everything we 17 | do toward this end ultimately depends on functions, functions are the natural place to begin." 18 | 19 | [[:section {:title "Functions as Data"}]] 20 | 21 | "Two things make Lisp functions different. One, mentioned above, is that Lisp itself is a collection of 22 | functions. This means that we can add to Lisp new operators of our own. Another important thing to know about 23 | functions is that they are Lisp objects. 24 | 25 | Lisp offers most of the data types one finds in other languages. We get integers and floating-point numbers, 26 | strings, arrays, structures, and so on. But Lisp supports one data type which may at first seem surprising: the 27 | function. Nearly all programming languages provide some form of function or procedure. What does it mean to say 28 | that Lisp provides them as a data type? It means that in Lisp we can do with functions all the things we expect 29 | to do with more familiar data types, like integers: create new ones at runtime, store them in variables and in 30 | structures, pass them as arguments to other functions, and return them as results. 31 | 32 | The ability to create and return functions at runtime is particularly useful. This might sound at first like a 33 | dubious sort of advantage, like the self-modifying machine language programs one can run on some computers. But 34 | creating new functions at runtime turns out to be a routinely used Lisp programming technique." 35 | 36 | [[:section {:title "Definding Functions"}]] 37 | 38 | "Most people first learn how to make functions with `defun`. The following expression defines a function called 39 | double which returns twice its argument." 40 | 41 | (defn double [x] (* x 2)) 42 | 43 | "Having fed this to Lisp, we can call double in other functions, or from the toplevel:" 44 | 45 | (fact 46 | 47 | > (double 1) 48 | 2 49 | 50 | (+ 1 2) 51 | => 3) 52 | 53 | "A file of Lisp code usually consists mainly of such defuns, and so resembles a file of procedure definitions 54 | in a language like C or Pascal. But something quite different is going on. Those defuns are not just procedure 55 | definitions, they're Lisp calls. This distinction will become clearer when we see what's going on underneath 56 | defun. 57 | 58 | Functions are objects in their own right. What defun really does is build one, and store it under the name 59 | given as the first argument. So as well as calling double, we can get hold of the function which implements it. 60 | The usual way to do so is by using the #' (sharp-quote) operator. This operator can be understood as mapping 61 | names to actual function objects. By affixing it to the name of double" 62 | 63 | (fact 64 | 65 | > (type #'double) 66 | clojure.lang.Var 67 | 68 | 69 | 70 | > ((fn [x] (* x 2)) 1) 71 | 2) 72 | -------------------------------------------------------------------------------- /example/test/documentation/short/t1_global.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t1-global) 2 | 3 | [[:global {:link {:auto-tag true} 4 | :tracking "UA-31320512-2"}]] 5 | 6 | [[:global {:link {:auto-number true}}]] 7 | -------------------------------------------------------------------------------- /example/test/documentation/short/t2_article.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t2-article) 2 | 3 | [[:article {:link {:auto-number true} 4 | :tracking "UA-31320512-2"}]] 5 | 6 | [[:article {:link {:auto-tag true}}]] 7 | -------------------------------------------------------------------------------- /example/test/documentation/short/t3_namespaces.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t3-namespaces) 2 | 3 | [[:ns {:refer documentation.short.t3-namespaces}]] 4 | -------------------------------------------------------------------------------- /example/test/documentation/short/t4_numbers.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t4-numbers) 2 | 3 | [[:chapter {:title "An Unexpected Party"}]] 4 | 5 | [[:section {:title "In a hole in the ground there lived a hobbit"}]] 6 | 7 | [[:section {:title "What on earth did I ask him to tea for"}]] 8 | 9 | [[:section {:title "I see they have begun to arrive already"}]] 10 | 11 | [[:section {:title "Now for some music"}]] 12 | 13 | [[:chapter {:title "Roast Mutton"}]] 14 | 15 | [[:chapter {:title "A Short Rest"}]] 16 | 17 | [[:chapter {:title "Over Hill And Under Hill"}]] 18 | 19 | [[:chapter {:title "Riddles In The Dark"}]] 20 | -------------------------------------------------------------------------------- /example/test/documentation/short/t5_references.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t5-references 2 | (:require [example.core])) 3 | 4 | [[:reference {:refer example.core/foo}]] 5 | -------------------------------------------------------------------------------- /example/test/documentation/short/t6_stencil.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t6-stencil 2 | (:require [example.core])) 3 | 4 | [[:chapter {:title "Hello There" :tag "hello"}]] 5 | 6 | [[:section {:title "Hello World" :tag "world"}]] 7 | 8 | "{{hello}} {{stencil/world}} {{PROJECT.version}}" 9 | -------------------------------------------------------------------------------- /example/test/documentation/short/t7_pluggable.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t7-pluggable 2 | (:require [example.core])) 3 | 4 | [[:random {:title "Hello There" :tag "hello"}]] 5 | -------------------------------------------------------------------------------- /example/test/documentation/short/t8_citation.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.short.t8-citation) 2 | 3 | "This is a case of [[2004-bower-thompson]]" 4 | 5 | [[:citation {:mode :url :tag "2012-bower-thompson"}]] 6 | 7 | [[:citation {:mode :url :tag "2004-bower-thompson"}]] 8 | 9 | [[:bibliography {:mode :full}]] 10 | -------------------------------------------------------------------------------- /example/test/example/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns example.core-test 2 | (:use clojure.test) 3 | (:require [example.core :refer :all])) 4 | 5 | ^{:refer example.core/foo :added "0.1"} 6 | (deftest foo-test 7 | (is (= 1 1))) 8 | -------------------------------------------------------------------------------- /hydrox.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/hydrox.graffle -------------------------------------------------------------------------------- /lein/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | /test/documentation 13 | /template 14 | /docs 15 | -------------------------------------------------------------------------------- /lein/docs/css/rdash.min.css: -------------------------------------------------------------------------------- 1 | #content-wrapper{padding-left:0;margin-left:0;width:100%;height:auto}@media only screen and (min-width:561px){#page-wrapper.open{padding-left:250px}}@media only screen and (max-width:560px){#page-wrapper.open{padding-left:70px}}#page-wrapper.open #sidebar-wrapper{left:150px}@media only screen and (max-width:560px){body.hamburg #page-wrapper{padding-left:0}body.hamburg #page-wrapper:not(.open) #sidebar-wrapper{position:absolute;left:-100px}body.hamburg #page-wrapper:not(.open) ul.sidebar .sidebar-title.separator{display:none}body.hamburg #page-wrapper.open #sidebar-wrapper{position:fixed}body.hamburg #page-wrapper.open #sidebar-wrapper ul.sidebar li.sidebar-main{margin-left:0}body.hamburg #sidebar-wrapper ul.sidebar li.sidebar-main,body.hamburg .row.header .meta{margin-left:70px}body.hamburg #page-wrapper.open #sidebar-wrapper ul.sidebar li.sidebar-main,body.hamburg #sidebar-wrapper ul.sidebar li.sidebar-main{transition:margin-left .4s ease 0s}}.row.header{height:60px;background:#fff;margin-bottom:15px}.row.header>div:last-child{padding-right:0}.row.header .meta .page{font-size:17px;padding-top:11px}.row.header .meta .breadcrumb-links{font-size:10px}.row.header .meta div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.row.header .login a{padding:18px;display:block}.row.header .user{min-width:130px}.row.header .user>.item{width:65px;height:60px;float:right;display:inline-block;text-align:center;vertical-align:middle}.row.header .user>.item a{color:#919191;display:block}.row.header .user>.item i{font-size:20px;line-height:55px}.row.header .user>.item img{width:40px;height:40px;margin-top:10px;border-radius:2px}.row.header .user>.item ul.dropdown-menu{border-radius:2px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.05);box-shadow:0 6px 12px rgba(0,0,0,.05)}.row.header .user>.item ul.dropdown-menu .dropdown-header{text-align:center}.row.header .user>.item ul.dropdown-menu li.link{text-align:left}.row.header .user>.item ul.dropdown-menu li.link a{padding-left:7px;padding-right:7px}.row.header .user>.item ul.dropdown-menu:before{position:absolute;top:-7px;right:23px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid rgba(0,0,0,.2);border-left:7px solid transparent;content:''}.row.header .user>.item ul.dropdown-menu:after{position:absolute;top:-6px;right:24px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.loading{width:40px;height:40px;position:relative;margin:100px auto}.double-bounce1,.double-bounce2{width:100%;height:100%;border-radius:50%;background-color:#333;opacity:.6;position:absolute;top:0;left:0;-webkit-animation:bounce 2s infinite ease-in-out;animation:bounce 2s infinite ease-in-out}.double-bounce2{-webkit-animation-delay:-1s;animation-delay:-1s}@-webkit-keyframes bounce{0%,100%{-webkit-transform:scale(0)}50%{-webkit-transform:scale(1)}}@keyframes bounce{0%,100%{transform:scale(0);-webkit-transform:scale(0)}50%{transform:scale(1);-webkit-transform:scale(1)}}@font-face{font-family:Montserrat;src:url(../fonts/montserrat-regular-webfont.eot);src:url(../fonts/montserrat-regular-webfont.eot?#iefix) format('embedded-opentype'),url(../fonts/montserrat-regular-webfont.woff) format('woff'),url(../fonts/montserrat-regular-webfont.ttf) format('truetype'),url(../fonts/montserrat-regular-webfont.svg#montserratregular) format('svg');font-weight:400;font-style:normal}@media screen and (-webkit-min-device-pixel-ratio:0){@font-face{font-family:Montserrat;src:url(../fonts/montserrat-regular-webfont.svg) format('svg')}select{font-family:Arial,Helvetica,sans-serif}}html{overflow-y:scroll}body{background:#f3f3f3;font-family:Montserrat;color:#333!important}.row{margin-left:0!important;margin-right:0!important}.row>div{margin-bottom:15px}.alerts-container .alert:last-child{margin-bottom:0}#page-wrapper{padding-left:70px;height:100%}#sidebar-wrapper{margin-left:-150px;left:-30px;width:250px;position:fixed;height:100%;z-index:999}#page-wrapper,#sidebar-wrapper{transition:all .4s ease 0s}.green{background:#23ae89!important}.blue{background:#2361ae!important}.orange{background:#d3a938!important}.red{background:#ae2323!important}.form-group .help-block.form-group-inline-message{padding-top:5px}div.input-mask{padding-top:7px}#sidebar-wrapper{background:#30426a}#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator,.sidebar-footer,ul.sidebar .sidebar-list a:hover,ul.sidebar .sidebar-main a{background:#2d3e63}ul.sidebar{position:absolute;top:0;bottom:0;padding:0;margin:0;list-style:none;text-indent:20px;overflow-x:hidden;overflow-y:auto}ul.sidebar li a{color:#fff;display:block;float:left;text-decoration:none;width:250px}ul.sidebar .sidebar-main{height:65px}ul.sidebar .sidebar-main a{font-size:18px;line-height:60px}ul.sidebar .sidebar-main .menu-icon{float:right;font-size:18px;padding-right:28px;line-height:60px}ul.sidebar .sidebar-title{color:#738bc0;font-size:12px;height:35px;line-height:40px;text-transform:uppercase;transition:all .6s ease 0s}ul.sidebar .sidebar-list{height:40px}ul.sidebar .sidebar-list a{text-indent:25px;font-size:15px;color:#b2bfdc;line-height:40px}ul.sidebar .sidebar-list a:hover{color:#fff;border-left:3px solid #e99d1a;text-indent:22px}ul.sidebar .sidebar-list a:hover .menu-icon{text-indent:25px}ul.sidebar .sidebar-list .menu-icon{float:right;padding-right:29px;line-height:40px;width:70px}#page-wrapper:not(.open) ul.sidebar{bottom:0}#page-wrapper:not(.open) ul.sidebar .sidebar-title{display:none;height:0;text-indent:-100px}#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator{display:block;height:2px;margin:13px 0}#page-wrapper:not(.open) ul.sidebar .sidebar-list a:hover span{border-left:3px solid #e99d1a;text-indent:22px}#page-wrapper:not(.open) .sidebar-footer{display:none}.sidebar-footer{position:absolute;height:40px;bottom:0;width:100%;padding:0;margin:0;transition:all .6s ease 0s;text-align:center}.sidebar-footer div a{color:#b2bfdc;font-size:12px;line-height:43px}.sidebar-footer div a:hover{color:#fff;text-decoration:none}.widget{-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);-moz-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05);background:#fff;border:1px solid transparent;border-radius:2px;border-color:#e9e9e9}.widget .widget-footer .pagination,.widget .widget-header .pagination{margin:0}.widget .widget-header{color:#767676;background-color:#f6f6f6;padding:10px 15px;border-bottom:1px solid #e9e9e9;line-height:30px}.widget .widget-header i{margin-right:5px}.widget .widget-body{padding:20px}.widget .widget-body table thead{background:#fafafa}.widget .widget-body table thead *{font-size:14px!important}.widget .widget-body table tbody *{font-size:13px!important}.widget .widget-body .error{color:red}.widget .widget-body button{margin-left:5px}.widget .widget-body div.alert{margin-bottom:10px}.widget .widget-body.large{height:350px;overflow-y:auto}.widget .widget-body.medium{height:250px;overflow-y:auto}.widget .widget-body.small{height:150px;overflow-y:auto}.widget .widget-body.no-padding{padding:0}.widget .widget-body.no-padding .error,.widget .widget-body.no-padding .message{padding:20px}.widget .widget-icon{background:#30426a;width:65px;height:65px;border-radius:50%;text-align:center;vertical-align:middle;margin-right:15px}.widget .widget-icon i{line-height:66px;color:#fff;font-size:30px}.widget .widget-footer{border-top:1px solid #e9e9e9;padding:10px}.widget .widget-footer .pagination,.widget .widget-title .pagination{margin:0}.widget .widget-content .title{font-size:28px;display:block} -------------------------------------------------------------------------------- /lein/docs/css/scrollspy.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background: #f8f8f8; 4 | position: relative; 5 | line-height: 1.5; 6 | font-family: 'Raleway', sans-serif; 7 | } 8 | 9 | h1, h2 { 10 | font-weight: 700; 11 | } 12 | 13 | h2.chapter{ 14 | font-weight: 900; 15 | margin-top: 50px; 16 | } 17 | 18 | h3 span { 19 | color: #286090; 20 | } 21 | 22 | .jumbotron { 23 | border-radius: 0px; 24 | background: #ffffff; 25 | margin-bottom: 0; 26 | text-align: center; 27 | border: 1px solid transparent; 28 | background: #ffffff; 29 | border: 1px solid; 30 | border-radius: 2px; 31 | border-color: #e9e9e9; 32 | } 33 | 34 | .jumbotron button { 35 | margin-top: 10px; 36 | } 37 | 38 | .chapter > div{ 39 | margin-top: 30px; 40 | margin-bottom: 10px; 41 | padding: 20px; 42 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 43 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 44 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 45 | background: #ffffff; 46 | border: 1px solid; 47 | border-radius: 2px; 48 | border-color: #e9e9e9; 49 | 50 | } 51 | 52 | section.section { 53 | margin-top: 30px; 54 | margin-bottom: 10px; 55 | padding: 20px; 56 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 57 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 58 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 59 | background: #ffffff; 60 | border: 1px solid transparent; 61 | border-radius: 2px; 62 | border-color: #e9e9e9; 63 | } 64 | 65 | section.subsection { 66 | margin-top: 30px; 67 | } 68 | 69 | section:last-child { 70 | border-bottom: none; 71 | } 72 | 73 | #content{ 74 | margin-top: 30px; 75 | } 76 | 77 | #nav{ 78 | margin-top: 50px; 79 | } 80 | 81 | 82 | .nav { 83 | background: #30426a; 84 | } 85 | 86 | .nav .contents{ 87 | color: white; 88 | padding: 18px 0px 15px 30px; 89 | background: #2d3e63; 90 | } 91 | 92 | .nav a { 93 | color: #b2bfdc; 94 | font-style: italic; 95 | } 96 | 97 | .nav li a:hover, 98 | .nav li a:focus { 99 | background: #2d3e63; 100 | } 101 | 102 | .nav .active { 103 | font-weight: bold; 104 | /*background: #72bcd4;*/ 105 | } 106 | 107 | .nav .nav { 108 | display: none; 109 | } 110 | 111 | .nav .active .nav { 112 | display: block; 113 | } 114 | 115 | .nav>li>a { 116 | padding-top: 5px; 117 | padding-left: 30px; 118 | } 119 | .nav .nav a { 120 | font-weight: normal; 121 | text-decoration: none; 122 | font-size: .85em; 123 | padding-left: 40px; 124 | } 125 | 126 | .nav .nav span { 127 | margin: 0 5px 0 2px; 128 | } 129 | 130 | .nav .nav .active a, 131 | .nav .nav .active:hover a, 132 | .nav .nav .active:focus a { 133 | text-decoration: underline; 134 | /*padding-left: 30px; 135 | border-left: 5px solid black;*/ 136 | } 137 | 138 | .nav .nav .active span, 139 | .nav .nav .active:hover span, 140 | .nav .nav .active:focus span { 141 | display: none; 142 | } 143 | 144 | .application { 145 | border-top: 1px solid #c1e1ec; 146 | } 147 | 148 | .affix-top { 149 | position: relative; 150 | } 151 | 152 | .affix { 153 | top: 0px; 154 | } 155 | 156 | .affix, 157 | .affix-bottom { 158 | /*width: 213px;*/ 159 | } 160 | 161 | .affix-bottom { 162 | position: absolute; 163 | } 164 | 165 | footer { 166 | border-top: 1px solid #c1e1ec; 167 | height: 50px; 168 | } 169 | 170 | footer p { 171 | line-height: 50px; 172 | margin-bottom: 0; 173 | } 174 | 175 | @media (min-width:1200px) { 176 | .affix, 177 | .affix-bottom { 178 | width: 263px; 179 | } 180 | } 181 | 182 | .img img { 183 | display: block; 184 | margin-top: 10px; 185 | margin-right: auto; 186 | margin-left: auto; 187 | } 188 | 189 | .figure { 190 | border: solid 1px #ccc; 191 | padding: 10px; 192 | margin-bottom: 10px; 193 | } 194 | 195 | .figure h4{ 196 | text-align: center; 197 | margin-top:30px 198 | } 199 | 200 | .code h5{ 201 | font-weight: 900; 202 | margin-top: 20px; 203 | margin-bottom: 3px; 204 | } 205 | 206 | 207 | ul.sidebar .submenu-item { 208 | margin-left: 10px; 209 | } 210 | 211 | nav { 212 | background-color: #30426A; 213 | border-color: #30426A; 214 | } 215 | 216 | .navbar-brand img{ 217 | height: 45px; 218 | margin-top: -10px; 219 | } 220 | 221 | .navbar-nav { 222 | margin-top: 12px; 223 | } 224 | 225 | .nav .open>a, .nav .open>a:focus, .nav .open>a:hover { 226 | background-color: #30426A; 227 | } 228 | 229 | .dropdown-menu>li>a:hover { 230 | color: white; 231 | } 232 | -------------------------------------------------------------------------------- /lein/docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/docs/img/favicon.png -------------------------------------------------------------------------------- /lein/docs/img/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/docs/img/logo-white.png -------------------------------------------------------------------------------- /lein/docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/docs/img/logo.png -------------------------------------------------------------------------------- /lein/docs/js/angular-highlightjs.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-highlightjs 2 | version: 0.4.1 3 | build date: 2015-02-03 4 | author: Chih-Hsuan Fan 5 | https://github.com/pc035860/angular-highlightjs.git */ 6 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="hljs"),function(a,b){function c(a){var c=!0;return b.forEach(["source","include"],function(b){a[b]&&(c=!1)}),c}var d=b.module("hljs",[]);d.provider("hljsService",function(){var a={};return{setOptions:function(c){b.extend(a,c)},getOptions:function(){return b.copy(a)},$get:["$window",function(c){return(c.hljs.configure||b.noop)(a),c.hljs}]}}),d.factory("hljsCache",["$cacheFactory",function(a){return a("hljsCache")}]),d.controller("HljsCtrl",["hljsCache","hljsService",function(a,c){var d=this,e=null,f=null,g=null,h=null;d.init=function(a){e=a},d.setLanguage=function(a){f=a,g&&d.highlight(g)},d.highlightCallback=function(a){h=a},d.highlight=function(i){if(e){var j,k;g=i,f?(k=d._cacheKey(f,g),j=a.get(k),j||(j=c.highlight(f,c.fixMarkup(g),!0),a.put(k,j))):(k=d._cacheKey(g),j=a.get(k),j||(j=c.highlightAuto(c.fixMarkup(g)),a.put(k,j))),e.html(j.value),e.addClass(j.language),null!==h&&b.isFunction(h)&&h()}},d.clear=function(){e&&(g=null,e.text(""))},d.release=function(){e=null},d._cacheKey=function(){var a=Array.prototype.slice.call(arguments),b="!angular-highlightjs!";return a.join(b)}}]);var e,f,g,h;e=["$compile","$parse",function(a,d){return{restrict:"EA",controller:"HljsCtrl",compile:function(e){var f=e[0].innerHTML.replace(/^(\r\n|\r|\n)/m,""),g=e[0].textContent.replace(/^(\r\n|\r|\n)/m,"");return e.html('
'),function(e,h,i,j){var k,l;if(b.isDefined(i.compile)&&(k=d(i.compile)),b.isDefined(i.escape)?l=d(i.escape):b.isDefined(i.noEscape)&&(l=d("false")),j.init(h.find("code")),i.onhighlight&&j.highlightCallback(function(){e.$eval(i.onhighlight)}),(f||g)&&c(i)){var m;m=l&&!l(e)?g:f,j.highlight(m),k&&k(e)&&a(h.find("code").contents())(e)}e.$on("$destroy",function(){j.release()})}}}}],f=function(a){return[function(){return{require:"?hljs",restrict:"A",link:function(c,d,e,f){f&&e.$observe(a,function(a){b.isDefined(a)&&f.setLanguage(a)})}}}]},g=function(a){return["$compile","$parse",function(c,d){return{require:"?hljs",restrict:"A",link:function(e,f,g,h){var i;h&&(b.isDefined(g.compile)&&(i=d(g.compile)),e.$watch(g[a],function(a){a?(h.highlight(a),i&&i(e)&&c(f.find("code").contents())(e)):h.clear()}))}}}]},h=function(a){return["$http","$templateCache","$q","$compile","$parse",function(c,d,e,f,g){return{require:"?hljs",restrict:"A",compile:function(h,i){var j=i[a];return function(a,h,i,k){var l,m=0;k&&(b.isDefined(i.compile)&&(l=g(i.compile)),a.$watch(j,function(g){var i=++m;if(g&&b.isString(g)){var j,n;j=d.get(g),j||(n=e.defer(),c.get(g,{cache:d,transformResponse:function(a){return a}}).success(function(a){i===m&&n.resolve(a)}).error(function(){i===m&&k.clear(),n.resolve()}),j=n.promise),e.when(j).then(function(c){c&&(b.isArray(c)?c=c[1]:b.isObject(c)&&(c=c.data),c=c.replace(/^(\r\n|\r|\n)/m,""),k.highlight(c),l&&l(a)&&f(h.find("code").contents())(a))})}else k.clear()}))}}}}]},d.directive("hljs",e).directive("language",f("language")).directive("source",g("source")).directive("include",h("include"))}(window,window.angular); -------------------------------------------------------------------------------- /lein/docs/sample-document.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | a sample document 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 64 | 65 | 66 |
67 |
68 | 71 |
72 |
73 |
74 |
75 |

a sample document

76 |

generating a document from code

77 |
78 |
79 |

1    Introduction

This is an introduction to the exciting library

1.1    Defining a function

We define function add-5

(defn add-5 [x] 80 | (+ x 5))

1.2    Testing a function

add-5 outputs the following results seen in e.1.2 and e.1.3:

e.1.1
(+ 1 0) => 1 81 | (+ 1 3) => 4
(+ 1 1) => 2 82 | (+ 1 3) => 2
e.1.2  -  1 add 5 equals 6
(add-5 1) => 6
e.1.3  -  10 add 5 equals 15
(add-5 10) => 15

2    Walkthrough

Here is a walkthrough for the library

3    API Reference

The API reference here:

83 |
84 |
85 | 86 | 87 | 88 | 95 | 96 | 97 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /lein/project.clj: -------------------------------------------------------------------------------- 1 | (defproject lein-hydrox "0.1.16" 2 | :description "leiningen plugin for hydrox" 3 | :url "https://github.com/helpshift/hydrox" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[helpshift/hydrox "0.1.16"]] 7 | :eval-in-leiningen true) 8 | -------------------------------------------------------------------------------- /lein/resources/hydrox/sample.edn: -------------------------------------------------------------------------------- 1 | :documentation {:site "sample" 2 | :output "docs" 3 | :template {:path "template" 4 | :copy ["assets"] 5 | :defaults {:template "article.html" 6 | :navbar [:file "partials/navbar.html"] 7 | :dependencies [:file "partials/deps-web.html"] 8 | :navigation :navigation 9 | :article :article}} 10 | :paths ["test/documentation"] 11 | :files {"sample-document" 12 | {:input "test/documentation/sample_document.clj" 13 | :title "a sample document" 14 | :subtitle "generating a document from code"}} 15 | :link {:auto-tag true 16 | :auto-number true}} 17 | -------------------------------------------------------------------------------- /lein/resources/hydrox/template/article.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <@=title> 11 | 12 | <@=dependencies> 13 | 14 | 19 | 20 | 23 | 24 | 25 | 26 | <@=navbar> 27 | 28 |
29 |
30 | 33 |
34 |
35 |
36 |
37 |

<@=title>

38 |

<@=subtitle>

39 |
40 |
41 | <@=article> 42 |
43 |
44 | 45 | 46 | 47 | 54 | 55 | 56 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /lein/resources/hydrox/template/assets/css/scrollspy.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background: #f8f8f8; 4 | position: relative; 5 | line-height: 1.5; 6 | font-family: 'Raleway', sans-serif; 7 | } 8 | 9 | h1, h2 { 10 | font-weight: 700; 11 | } 12 | 13 | h2.chapter{ 14 | font-weight: 900; 15 | margin-top: 50px; 16 | } 17 | 18 | h3 span { 19 | color: #286090; 20 | } 21 | 22 | .jumbotron { 23 | border-radius: 0px; 24 | background: #ffffff; 25 | margin-bottom: 0; 26 | text-align: center; 27 | border: 1px solid transparent; 28 | background: #ffffff; 29 | border: 1px solid; 30 | border-radius: 2px; 31 | border-color: #e9e9e9; 32 | } 33 | 34 | .jumbotron button { 35 | margin-top: 10px; 36 | } 37 | 38 | .chapter > div{ 39 | margin-top: 30px; 40 | margin-bottom: 10px; 41 | padding: 20px; 42 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 43 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 44 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 45 | background: #ffffff; 46 | border: 1px solid; 47 | border-radius: 2px; 48 | border-color: #e9e9e9; 49 | 50 | } 51 | 52 | section.section { 53 | margin-top: 30px; 54 | margin-bottom: 10px; 55 | padding: 20px; 56 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 57 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 58 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 59 | background: #ffffff; 60 | border: 1px solid transparent; 61 | border-radius: 2px; 62 | border-color: #e9e9e9; 63 | } 64 | 65 | section.subsection { 66 | margin-top: 30px; 67 | } 68 | 69 | section:last-child { 70 | border-bottom: none; 71 | } 72 | 73 | #content{ 74 | margin-top: 30px; 75 | } 76 | 77 | #nav{ 78 | margin-top: 50px; 79 | } 80 | 81 | 82 | .nav { 83 | background: #30426a; 84 | } 85 | 86 | .nav .contents{ 87 | color: white; 88 | padding: 18px 0px 15px 30px; 89 | background: #2d3e63; 90 | } 91 | 92 | .nav a { 93 | color: #b2bfdc; 94 | font-style: italic; 95 | } 96 | 97 | .nav li a:hover, 98 | .nav li a:focus { 99 | background: #2d3e63; 100 | } 101 | 102 | .nav .active { 103 | font-weight: bold; 104 | /*background: #72bcd4;*/ 105 | } 106 | 107 | .nav .nav { 108 | display: none; 109 | } 110 | 111 | .nav .active .nav { 112 | display: block; 113 | } 114 | 115 | .nav>li>a { 116 | padding-top: 5px; 117 | padding-left: 30px; 118 | } 119 | .nav .nav a { 120 | font-weight: normal; 121 | text-decoration: none; 122 | font-size: .85em; 123 | padding-left: 40px; 124 | } 125 | 126 | .nav .nav span { 127 | margin: 0 5px 0 2px; 128 | } 129 | 130 | .nav .nav .active a, 131 | .nav .nav .active:hover a, 132 | .nav .nav .active:focus a { 133 | text-decoration: underline; 134 | /*padding-left: 30px; 135 | border-left: 5px solid black;*/ 136 | } 137 | 138 | .nav .nav .active span, 139 | .nav .nav .active:hover span, 140 | .nav .nav .active:focus span { 141 | display: none; 142 | } 143 | 144 | .application { 145 | border-top: 1px solid #c1e1ec; 146 | } 147 | 148 | .affix-top { 149 | position: relative; 150 | } 151 | 152 | .affix { 153 | top: 0px; 154 | } 155 | 156 | .affix, 157 | .affix-bottom { 158 | /*width: 213px;*/ 159 | } 160 | 161 | .affix-bottom { 162 | position: absolute; 163 | } 164 | 165 | footer { 166 | border-top: 1px solid #c1e1ec; 167 | height: 50px; 168 | } 169 | 170 | footer p { 171 | line-height: 50px; 172 | margin-bottom: 0; 173 | } 174 | 175 | @media (min-width:1200px) { 176 | .affix, 177 | .affix-bottom { 178 | width: 263px; 179 | } 180 | } 181 | 182 | .img img { 183 | display: block; 184 | margin-top: 10px; 185 | margin-right: auto; 186 | margin-left: auto; 187 | } 188 | 189 | .figure { 190 | border: solid 1px #ccc; 191 | padding: 10px; 192 | margin-bottom: 10px; 193 | } 194 | 195 | .figure h4{ 196 | text-align: center; 197 | margin-top:30px 198 | } 199 | 200 | .code h5{ 201 | font-weight: 900; 202 | margin-top: 20px; 203 | margin-bottom: 3px; 204 | } 205 | 206 | 207 | ul.sidebar .submenu-item { 208 | margin-left: 10px; 209 | } 210 | 211 | nav { 212 | background-color: #30426A; 213 | border-color: #30426A; 214 | } 215 | 216 | .navbar-brand img{ 217 | height: 45px; 218 | margin-top: -10px; 219 | } 220 | 221 | .navbar-nav { 222 | margin-top: 12px; 223 | } 224 | 225 | .nav .open>a, .nav .open>a:focus, .nav .open>a:hover { 226 | background-color: #30426A; 227 | } 228 | 229 | .dropdown-menu>li>a:hover { 230 | color: white; 231 | } 232 | -------------------------------------------------------------------------------- /lein/resources/hydrox/template/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/resources/hydrox/template/assets/img/favicon.png -------------------------------------------------------------------------------- /lein/resources/hydrox/template/assets/img/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/resources/hydrox/template/assets/img/logo-white.png -------------------------------------------------------------------------------- /lein/resources/hydrox/template/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/resources/hydrox/template/assets/img/logo.png -------------------------------------------------------------------------------- /lein/resources/hydrox/template/assets/js/angular-highlightjs.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-highlightjs 2 | version: 0.4.1 3 | build date: 2015-02-03 4 | author: Chih-Hsuan Fan 5 | https://github.com/pc035860/angular-highlightjs.git */ 6 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="hljs"),function(a,b){function c(a){var c=!0;return b.forEach(["source","include"],function(b){a[b]&&(c=!1)}),c}var d=b.module("hljs",[]);d.provider("hljsService",function(){var a={};return{setOptions:function(c){b.extend(a,c)},getOptions:function(){return b.copy(a)},$get:["$window",function(c){return(c.hljs.configure||b.noop)(a),c.hljs}]}}),d.factory("hljsCache",["$cacheFactory",function(a){return a("hljsCache")}]),d.controller("HljsCtrl",["hljsCache","hljsService",function(a,c){var d=this,e=null,f=null,g=null,h=null;d.init=function(a){e=a},d.setLanguage=function(a){f=a,g&&d.highlight(g)},d.highlightCallback=function(a){h=a},d.highlight=function(i){if(e){var j,k;g=i,f?(k=d._cacheKey(f,g),j=a.get(k),j||(j=c.highlight(f,c.fixMarkup(g),!0),a.put(k,j))):(k=d._cacheKey(g),j=a.get(k),j||(j=c.highlightAuto(c.fixMarkup(g)),a.put(k,j))),e.html(j.value),e.addClass(j.language),null!==h&&b.isFunction(h)&&h()}},d.clear=function(){e&&(g=null,e.text(""))},d.release=function(){e=null},d._cacheKey=function(){var a=Array.prototype.slice.call(arguments),b="!angular-highlightjs!";return a.join(b)}}]);var e,f,g,h;e=["$compile","$parse",function(a,d){return{restrict:"EA",controller:"HljsCtrl",compile:function(e){var f=e[0].innerHTML.replace(/^(\r\n|\r|\n)/m,""),g=e[0].textContent.replace(/^(\r\n|\r|\n)/m,"");return e.html('
'),function(e,h,i,j){var k,l;if(b.isDefined(i.compile)&&(k=d(i.compile)),b.isDefined(i.escape)?l=d(i.escape):b.isDefined(i.noEscape)&&(l=d("false")),j.init(h.find("code")),i.onhighlight&&j.highlightCallback(function(){e.$eval(i.onhighlight)}),(f||g)&&c(i)){var m;m=l&&!l(e)?g:f,j.highlight(m),k&&k(e)&&a(h.find("code").contents())(e)}e.$on("$destroy",function(){j.release()})}}}}],f=function(a){return[function(){return{require:"?hljs",restrict:"A",link:function(c,d,e,f){f&&e.$observe(a,function(a){b.isDefined(a)&&f.setLanguage(a)})}}}]},g=function(a){return["$compile","$parse",function(c,d){return{require:"?hljs",restrict:"A",link:function(e,f,g,h){var i;h&&(b.isDefined(g.compile)&&(i=d(g.compile)),e.$watch(g[a],function(a){a?(h.highlight(a),i&&i(e)&&c(f.find("code").contents())(e)):h.clear()}))}}}]},h=function(a){return["$http","$templateCache","$q","$compile","$parse",function(c,d,e,f,g){return{require:"?hljs",restrict:"A",compile:function(h,i){var j=i[a];return function(a,h,i,k){var l,m=0;k&&(b.isDefined(i.compile)&&(l=g(i.compile)),a.$watch(j,function(g){var i=++m;if(g&&b.isString(g)){var j,n;j=d.get(g),j||(n=e.defer(),c.get(g,{cache:d,transformResponse:function(a){return a}}).success(function(a){i===m&&n.resolve(a)}).error(function(){i===m&&k.clear(),n.resolve()}),j=n.promise),e.when(j).then(function(c){c&&(b.isArray(c)?c=c[1]:b.isObject(c)&&(c=c.data),c=c.replace(/^(\r\n|\r|\n)/m,""),k.highlight(c),l&&l(a)&&f(h.find("code").contents())(a))})}else k.clear()}))}}}}]},d.directive("hljs",e).directive("language",f("language")).directive("source",g("source")).directive("include",h("include"))}(window,window.angular); -------------------------------------------------------------------------------- /lein/resources/hydrox/template/partials/deps-web.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lein/resources/hydrox/template/partials/navbar.html: -------------------------------------------------------------------------------- 1 | 2 | 21 | -------------------------------------------------------------------------------- /lein/resources/hydrox/test/documentation/sample_document.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.sample-document 2 | (:require [midje.sweet :refer :all])) 3 | 4 | [[:chapter {:tag "hello" :title "Introduction"}]] 5 | 6 | "This is an introduction to the exciting library" 7 | 8 | [[:section {:title "Defining a function"}]] 9 | 10 | "We define function `add-5`" 11 | 12 | [[{:numbered false}]] 13 | (defn add-5 [x] 14 | (+ x 5)) 15 | 16 | [[:section {:title "Testing a function"}]] 17 | 18 | "`add-5` outputs the following results seen in 19 | [e.{{add-5-1}}](#add-5-1) and [e.{{add-5-10}}](#add-5-10):" 20 | 21 | [[{:tag "eq1"}]] 22 | (comment 23 | (+ 1 0) => 1 24 | (+ 1 3) => 4) 25 | 26 | ;;[[{:tag "eq2" :title "some equations 2"}]] 27 | (comment 28 | (+ 1 1) => 2 29 | (+ 1 3) => 2) 30 | 31 | [[{:tag "add-5-1" :title "1 add 5 equals 6"}]] 32 | (add-5 1) 33 | => 6 34 | 35 | [[{:tag "add-5-10" :title "10 add 5 equals 15"}]] 36 | (add-5 10) 37 | => 15 38 | 39 | [[:chapter {:title "Walkthrough"}]] 40 | 41 | "Here is a walkthrough for the library" 42 | 43 | [[:chapter {:title "API Reference"}]] 44 | 45 | "The API reference here:" 46 | -------------------------------------------------------------------------------- /lein/src/leiningen/hydrox.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.hydrox 2 | (:require [hydrox.core :as hydrox] 3 | [hydrox.meta :as meta] 4 | [hydrox.common.util :as util] 5 | [leiningen.hydrox.init :as init] 6 | [leiningen.hydrox.setup :as setup])) 7 | 8 | (defn hydrox 9 | " 10 | metadata and documentation management: 11 | 12 | usage: 13 | lein hydrox (watch) - default, watches project for changes and updates documentation accordingly 14 | lein hydrox init - initialises a project scaffold 15 | lein hydrox docs - generates documentation from project 16 | lein hydrox import - imports docstrings from test files 17 | lein hydrox purge - purges docstrings from code 18 | " 19 | [project & args] 20 | (with-redefs 21 | [clojure.tools.reader.edn/read-keyword leiningen.hydrox.setup/read-keyword] 22 | (case (first args) 23 | nil (hydrox project "watch") 24 | "init" (init/init project) 25 | "watch" (do (hydrox/dive) 26 | (hydrox/generate-docs) 27 | (Thread/sleep 10000000000)) 28 | "docs" (hydrox/generate-docs (hydrox/single-use)) 29 | "import" (hydrox/import-docstring (hydrox/single-use)) 30 | "purge" (meta/purge-project (util/read-project)) 31 | "help" (println (:doc (meta #'hydrox))) 32 | (hydrox project "help")))) 33 | 34 | (comment 35 | 36 | (require '[leiningen.core.project :as project]) 37 | 38 | (def project (project/read)) 39 | (hydrox project "init") 40 | (hydrox project "docs") 41 | 42 | 43 | ) 44 | -------------------------------------------------------------------------------- /lein/src/leiningen/hydrox/init.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.hydrox.init 2 | (:require [rewrite-clj.zip :as zip] 3 | [rewrite-clj.node :as node] 4 | [clojure.java.io :as io] 5 | [me.raynes.fs :as fs])) 6 | 7 | (def +template+ 8 | {:directories 9 | ["test/documentation" 10 | "template/assets/css" 11 | "template/assets/img" 12 | "template/assets/js" 13 | "template/partials"] 14 | :files 15 | ["test/documentation/sample_document.clj" 16 | "template/assets/css/rdash.min.css" 17 | "template/assets/css/scrollspy.css" 18 | "template/assets/img/favicon.png" 19 | "template/assets/img/logo.png" 20 | "template/assets/img/logo-white.png" 21 | "template/assets/js/angular-highlightjs.min.js" 22 | "template/partials/deps-web.html" 23 | "template/partials/navbar.html" 24 | "template/article.html"]}) 25 | 26 | (defn init [project] 27 | 28 | ;; check to see if files exist 29 | (let [proj (zip/of-file (str (:root project) "/project.clj"))] 30 | (when-not (zip/find-depth-first 31 | proj 32 | (comp #(= :documentation %) zip/sexpr)) 33 | 34 | ;; copy files 35 | (doseq [dir (:directories +template+)] 36 | (fs/mkdirs (str (:root project) "/" dir))) 37 | (doseq [f (:files +template+)] 38 | (io/copy (io/input-stream (io/resource (str "hydrox/" f))) 39 | (io/file (:root project) f))) 40 | 41 | ;; place sample entry in project.clj 42 | (-> proj 43 | zip/down 44 | zip/rightmost* 45 | (zip/insert-right (node/newline-node "\n")) 46 | zip/rightmost* 47 | (zip/insert-left (node/newline-node "\n")) 48 | (zip/prepend-space 1) 49 | (zip/insert-left (node/token-node :documentation ":documentation")) 50 | (zip/insert-left (-> (io/resource "hydrox/sample.edn") 51 | (io/input-stream) 52 | (slurp) 53 | (zip/of-string) 54 | (zip/next) 55 | (zip/node))) 56 | (zip/->root-string) 57 | (->> (spit (str (:root project) "/project.clj"))))))) 58 | 59 | 60 | (comment 61 | (io/copy (io/file (io/resource (str "hydrox/" "template/article.html"))) 62 | (io/file "template/article.html"))) 63 | -------------------------------------------------------------------------------- /lein/src/leiningen/hydrox/setup.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.hydrox.setup 2 | (:require [clojure.tools.reader.edn :as edn] 3 | [clojure.tools.reader.impl.commons :as common] 4 | [clojure.tools.reader.impl.utils :as utils] 5 | [clojure.tools.reader.reader-types :as reader])) 6 | 7 | (defn read-keyword 8 | [reader initch opts] 9 | (let [ch (reader/read-char reader)] 10 | (if-not (utils/whitespace? ch) 11 | (let [token (#'edn/read-token reader ch)] 12 | (keyword token)) 13 | (reader/reader-error reader "Invalid token: :")))) 14 | 15 | (defn patch-read-keyword [] 16 | (alter-var-root #'clojure.tools.reader.edn/read-keyword 17 | (fn [_] 18 | read-keyword))) 19 | -------------------------------------------------------------------------------- /lein/template/article.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <@=title> 11 | 12 | <@=dependencies> 13 | 14 | 19 | 20 | 23 | 24 | 25 | 26 | <@=navbar> 27 | 28 |
29 |
30 | 33 |
34 |
35 |
36 |
37 |

<@=title>

38 |

<@=subtitle>

39 |
40 |
41 | <@=article> 42 |
43 |
44 | 45 | 46 | 47 | 54 | 55 | 56 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /lein/template/assets/css/scrollspy.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background: #f8f8f8; 4 | position: relative; 5 | line-height: 1.5; 6 | font-family: 'Raleway', sans-serif; 7 | } 8 | 9 | h1, h2 { 10 | font-weight: 700; 11 | } 12 | 13 | h2.chapter{ 14 | font-weight: 900; 15 | margin-top: 50px; 16 | } 17 | 18 | h3 span { 19 | color: #286090; 20 | } 21 | 22 | .jumbotron { 23 | border-radius: 0px; 24 | background: #ffffff; 25 | margin-bottom: 0; 26 | text-align: center; 27 | border: 1px solid transparent; 28 | background: #ffffff; 29 | border: 1px solid; 30 | border-radius: 2px; 31 | border-color: #e9e9e9; 32 | } 33 | 34 | .jumbotron button { 35 | margin-top: 10px; 36 | } 37 | 38 | .chapter > div{ 39 | margin-top: 30px; 40 | margin-bottom: 10px; 41 | padding: 20px; 42 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 43 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 44 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 45 | background: #ffffff; 46 | border: 1px solid; 47 | border-radius: 2px; 48 | border-color: #e9e9e9; 49 | 50 | } 51 | 52 | section.section { 53 | margin-top: 30px; 54 | margin-bottom: 10px; 55 | padding: 20px; 56 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 57 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 58 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 59 | background: #ffffff; 60 | border: 1px solid transparent; 61 | border-radius: 2px; 62 | border-color: #e9e9e9; 63 | } 64 | 65 | section.subsection { 66 | margin-top: 30px; 67 | } 68 | 69 | section:last-child { 70 | border-bottom: none; 71 | } 72 | 73 | #content{ 74 | margin-top: 30px; 75 | } 76 | 77 | #nav{ 78 | margin-top: 50px; 79 | } 80 | 81 | 82 | .nav { 83 | background: #30426a; 84 | } 85 | 86 | .nav .contents{ 87 | color: white; 88 | padding: 18px 0px 15px 30px; 89 | background: #2d3e63; 90 | } 91 | 92 | .nav a { 93 | color: #b2bfdc; 94 | font-style: italic; 95 | } 96 | 97 | .nav li a:hover, 98 | .nav li a:focus { 99 | background: #2d3e63; 100 | } 101 | 102 | .nav .active { 103 | font-weight: bold; 104 | /*background: #72bcd4;*/ 105 | } 106 | 107 | .nav .nav { 108 | display: none; 109 | } 110 | 111 | .nav .active .nav { 112 | display: block; 113 | } 114 | 115 | .nav>li>a { 116 | padding-top: 5px; 117 | padding-left: 30px; 118 | } 119 | .nav .nav a { 120 | font-weight: normal; 121 | text-decoration: none; 122 | font-size: .85em; 123 | padding-left: 40px; 124 | } 125 | 126 | .nav .nav span { 127 | margin: 0 5px 0 2px; 128 | } 129 | 130 | .nav .nav .active a, 131 | .nav .nav .active:hover a, 132 | .nav .nav .active:focus a { 133 | text-decoration: underline; 134 | /*padding-left: 30px; 135 | border-left: 5px solid black;*/ 136 | } 137 | 138 | .nav .nav .active span, 139 | .nav .nav .active:hover span, 140 | .nav .nav .active:focus span { 141 | display: none; 142 | } 143 | 144 | .application { 145 | border-top: 1px solid #c1e1ec; 146 | } 147 | 148 | .affix-top { 149 | position: relative; 150 | } 151 | 152 | .affix { 153 | top: 0px; 154 | } 155 | 156 | .affix, 157 | .affix-bottom { 158 | /*width: 213px;*/ 159 | } 160 | 161 | .affix-bottom { 162 | position: absolute; 163 | } 164 | 165 | footer { 166 | border-top: 1px solid #c1e1ec; 167 | height: 50px; 168 | } 169 | 170 | footer p { 171 | line-height: 50px; 172 | margin-bottom: 0; 173 | } 174 | 175 | @media (min-width:1200px) { 176 | .affix, 177 | .affix-bottom { 178 | width: 263px; 179 | } 180 | } 181 | 182 | .img img { 183 | display: block; 184 | margin-top: 10px; 185 | margin-right: auto; 186 | margin-left: auto; 187 | } 188 | 189 | .figure { 190 | border: solid 1px #ccc; 191 | padding: 10px; 192 | margin-bottom: 10px; 193 | } 194 | 195 | .figure h4{ 196 | text-align: center; 197 | margin-top:30px 198 | } 199 | 200 | .code h5{ 201 | font-weight: 900; 202 | margin-top: 20px; 203 | margin-bottom: 3px; 204 | } 205 | 206 | 207 | ul.sidebar .submenu-item { 208 | margin-left: 10px; 209 | } 210 | 211 | nav { 212 | background-color: #30426A; 213 | border-color: #30426A; 214 | } 215 | 216 | .navbar-brand img{ 217 | height: 45px; 218 | margin-top: -10px; 219 | } 220 | 221 | .navbar-nav { 222 | margin-top: 12px; 223 | } 224 | 225 | .nav .open>a, .nav .open>a:focus, .nav .open>a:hover { 226 | background-color: #30426A; 227 | } 228 | 229 | .dropdown-menu>li>a:hover { 230 | color: white; 231 | } 232 | -------------------------------------------------------------------------------- /lein/template/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/template/assets/img/favicon.png -------------------------------------------------------------------------------- /lein/template/assets/img/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/template/assets/img/logo-white.png -------------------------------------------------------------------------------- /lein/template/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/lein/template/assets/img/logo.png -------------------------------------------------------------------------------- /lein/template/assets/js/angular-highlightjs.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-highlightjs 2 | version: 0.4.1 3 | build date: 2015-02-03 4 | author: Chih-Hsuan Fan 5 | https://github.com/pc035860/angular-highlightjs.git */ 6 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="hljs"),function(a,b){function c(a){var c=!0;return b.forEach(["source","include"],function(b){a[b]&&(c=!1)}),c}var d=b.module("hljs",[]);d.provider("hljsService",function(){var a={};return{setOptions:function(c){b.extend(a,c)},getOptions:function(){return b.copy(a)},$get:["$window",function(c){return(c.hljs.configure||b.noop)(a),c.hljs}]}}),d.factory("hljsCache",["$cacheFactory",function(a){return a("hljsCache")}]),d.controller("HljsCtrl",["hljsCache","hljsService",function(a,c){var d=this,e=null,f=null,g=null,h=null;d.init=function(a){e=a},d.setLanguage=function(a){f=a,g&&d.highlight(g)},d.highlightCallback=function(a){h=a},d.highlight=function(i){if(e){var j,k;g=i,f?(k=d._cacheKey(f,g),j=a.get(k),j||(j=c.highlight(f,c.fixMarkup(g),!0),a.put(k,j))):(k=d._cacheKey(g),j=a.get(k),j||(j=c.highlightAuto(c.fixMarkup(g)),a.put(k,j))),e.html(j.value),e.addClass(j.language),null!==h&&b.isFunction(h)&&h()}},d.clear=function(){e&&(g=null,e.text(""))},d.release=function(){e=null},d._cacheKey=function(){var a=Array.prototype.slice.call(arguments),b="!angular-highlightjs!";return a.join(b)}}]);var e,f,g,h;e=["$compile","$parse",function(a,d){return{restrict:"EA",controller:"HljsCtrl",compile:function(e){var f=e[0].innerHTML.replace(/^(\r\n|\r|\n)/m,""),g=e[0].textContent.replace(/^(\r\n|\r|\n)/m,"");return e.html('
'),function(e,h,i,j){var k,l;if(b.isDefined(i.compile)&&(k=d(i.compile)),b.isDefined(i.escape)?l=d(i.escape):b.isDefined(i.noEscape)&&(l=d("false")),j.init(h.find("code")),i.onhighlight&&j.highlightCallback(function(){e.$eval(i.onhighlight)}),(f||g)&&c(i)){var m;m=l&&!l(e)?g:f,j.highlight(m),k&&k(e)&&a(h.find("code").contents())(e)}e.$on("$destroy",function(){j.release()})}}}}],f=function(a){return[function(){return{require:"?hljs",restrict:"A",link:function(c,d,e,f){f&&e.$observe(a,function(a){b.isDefined(a)&&f.setLanguage(a)})}}}]},g=function(a){return["$compile","$parse",function(c,d){return{require:"?hljs",restrict:"A",link:function(e,f,g,h){var i;h&&(b.isDefined(g.compile)&&(i=d(g.compile)),e.$watch(g[a],function(a){a?(h.highlight(a),i&&i(e)&&c(f.find("code").contents())(e)):h.clear()}))}}}]},h=function(a){return["$http","$templateCache","$q","$compile","$parse",function(c,d,e,f,g){return{require:"?hljs",restrict:"A",compile:function(h,i){var j=i[a];return function(a,h,i,k){var l,m=0;k&&(b.isDefined(i.compile)&&(l=g(i.compile)),a.$watch(j,function(g){var i=++m;if(g&&b.isString(g)){var j,n;j=d.get(g),j||(n=e.defer(),c.get(g,{cache:d,transformResponse:function(a){return a}}).success(function(a){i===m&&n.resolve(a)}).error(function(){i===m&&k.clear(),n.resolve()}),j=n.promise),e.when(j).then(function(c){c&&(b.isArray(c)?c=c[1]:b.isObject(c)&&(c=c.data),c=c.replace(/^(\r\n|\r|\n)/m,""),k.highlight(c),l&&l(a)&&f(h.find("code").contents())(a))})}else k.clear()}))}}}}]},d.directive("hljs",e).directive("language",f("language")).directive("source",g("source")).directive("include",h("include"))}(window,window.angular); -------------------------------------------------------------------------------- /lein/template/partials/deps-web.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lein/template/partials/navbar.html: -------------------------------------------------------------------------------- 1 | 2 | 21 | -------------------------------------------------------------------------------- /lein/test/documentation/sample_document.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.sample-document 2 | (:require [midje.sweet :refer :all] 3 | [hydrox.core :as hydrox])) 4 | 5 | [[:chapter {:tag "hello" :title "Introduction"}]] 6 | 7 | "This is an introduction to the exciting library" 8 | 9 | [[:section {:title "Defining a function"}]] 10 | 11 | "We define function `add-5`" 12 | 13 | [[{:numbered false}]] 14 | (defn add-5 [x] 15 | (+ x 5)) 16 | 17 | [[:section {:title "Testing a function"}]] 18 | 19 | "`add-5` outputs the following results seen in 20 | [e.{{add-5-1}}](#add-5-1) and [e.{{add-5-10}}](#add-5-10):" 21 | 22 | [[{:tag "eq1"}]] 23 | (fact 24 | (+ 1 0) => 1 25 | (+ 1 3) => 4) 26 | 27 | ;;[[{:tag "eq2" :title "some equations 2"}]] 28 | (comment 29 | (+ 1 1) => 2 30 | (+ 1 3) => 2) 31 | 32 | (facts 33 | [[{:tag "add-5-1" :title "1 add 5 equals 6"}]] 34 | (add-5 1) => 6 35 | 36 | [[{:tag "add-5-10" :title "10 add 5 equals 15"}]] 37 | (add-5 10) => 15) 38 | 39 | [[:chapter {:title "Walkthrough"}]] 40 | 41 | "Here is a walkthrough for the library" 42 | 43 | [[:chapter {:title "API Reference"}]] 44 | 45 | "The API reference here:" 46 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject helpshift/hydrox "0.1.16" 2 | :description "dive deeper into your code" 3 | :url "https://github.com/helpshift/hydrox" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"] 7 | [im.chit/jai "0.2.10"] 8 | [im.chit/hara.data "2.2.17"] 9 | [im.chit/hara.io.watch "2.2.17"] 10 | [im.chit/hara.common.checks "2.2.17"] 11 | [im.chit/hara.common.watch "2.2.17"] 12 | [im.chit/hara.component "2.2.17"] 13 | [im.chit/hara.concurrent.pipe "2.2.17"] 14 | [im.chit/hara.event "2.2.17"] 15 | [im.chit/hara.string "2.2.17"] 16 | [hiccup "1.0.5"] 17 | [markdown-clj "0.9.68"] 18 | [stencil "0.5.0"] 19 | [me.raynes/fs "1.4.6"]] 20 | :documentation {:site "hydrox" 21 | :output "docs" 22 | :template {:path "template" 23 | :copy ["assets"] 24 | :defaults {:template "article.html" 25 | :navbar [:file "partials/navbar.html"] 26 | :dependencies [:file "partials/deps-web.html"] 27 | :navigation :navigation 28 | :article :article}} 29 | :paths ["test/documentation"] 30 | :files {"sample-document" 31 | {:input "test/documentation/sample_document.clj" 32 | :title "a sample document" 33 | :subtitle "generating a document from code"} 34 | "index" 35 | {:input "test/documentation/hydrox_guide.clj" 36 | :title "hydrox" 37 | :subtitle "dive deeper into your code"}} 38 | :link {:auto-tag true 39 | :auto-number true}} 40 | 41 | :profiles {:dev {:dependencies [[midje "1.7.0"] 42 | [compojure "1.4.0"] 43 | [ring "1.4.0"] 44 | [clj-http "1.1.2"]] 45 | :plugins [[lein-midje "3.1.3"] 46 | [lein-ancient "0.6.7"] 47 | [lein-hydrox "0.1.14"]]}}) 48 | -------------------------------------------------------------------------------- /src/hydrox/analyse.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse 2 | (:require [hydrox.analyse 3 | [common :as common] 4 | test source] 5 | [hydrox.doc :as doc] 6 | [hydrox.common 7 | [data :as data] 8 | [util :as util]] 9 | [clojure.java.io :as io] 10 | [hara.data.diff :as diff]) 11 | (:import java.io.File)) 12 | 13 | (def access-paths 14 | [[[:source-paths] :source] 15 | [[:documentation :paths] :doc] 16 | [[:test-paths] :test]]) 17 | 18 | (defn canonical 19 | "returns the canonical system path 20 | 21 | (canonical \"src\") 22 | => (str (System/getProperty \"user.dir\") \"/src\")" 23 | {:added "0.1"} 24 | [path] 25 | (.getCanonicalPath (io/as-file path))) 26 | 27 | (defn file-type 28 | "returns the file-type for entries 29 | 30 | (file-type {:source-paths [\"src\"] 31 | :test-paths [\"test\"]} (io/file \"src/code.clj\")) 32 | => :source" 33 | {:added "0.1"} 34 | [project ^File file] 35 | (let [path (.getCanonicalPath file)] 36 | (or (->> access-paths 37 | (keep (fn [[v res]] 38 | (if (some (fn [^String x] (<= 0 (.indexOf path x))) 39 | (get-in project v)) 40 | res))) 41 | (first)) 42 | 43 | (if (= path (str (:root project) "/" "project.clj")) 44 | :project) 45 | 46 | (do (println "IGNORE" file) 47 | :ignore)))) 48 | 49 | (defn add-code 50 | [{:keys [project] :as folio} ^File file type] 51 | (let [fkey (.getCanonicalPath file) 52 | registry (get-in folio [:registry fkey]) 53 | result (common/analyse-file type file project) 54 | diff (diff/diff result registry) 55 | folio (-> folio 56 | (assoc-in [:registry fkey] result) 57 | (update-in [:references] diff/patch diff)) 58 | plus (concat (-> diff :+ keys) (-> diff :> keys)) 59 | minus (-> diff :- keys) ] 60 | (if (not-empty plus) (println "Associating:" plus)) 61 | (if (not-empty minus) (println "Deleting:" minus)) 62 | (if (= type :source) 63 | (assoc-in folio [:namespace-lu (first (keys result))] fkey) 64 | folio))) 65 | 66 | (defn add-documentation [{:keys [project] :as folio} file] 67 | (if (not (:initialisation folio)) 68 | (let [inputs (-> project :documentation :files) 69 | ridx (reduce-kv (fn [out k v] 70 | (assoc out (:input v) k)) 71 | {} inputs) 72 | choice (->> (keys ridx) 73 | (filter (fn [suffix] (.endsWith (str file) suffix))) 74 | (first))] 75 | (if (and choice (not (:manual folio))) 76 | (doc/render-single folio (get ridx choice))))) 77 | folio) 78 | 79 | (defn add-project [{:keys [project] :as folio} file] 80 | (let [folio (-> file 81 | (util/read-project) 82 | (select-keys [:version :documentation]) 83 | (->> (update-in folio [:project] merge)))] 84 | (println "Project Updated") 85 | (if-not (:manual folio) 86 | (doc/render-all folio)) 87 | folio)) 88 | 89 | (defn add-file 90 | "adds a file to the folio 91 | (-> {:project (util/read-project (io/file \"example/project.clj\"))} 92 | (add-file (io/file \"example/test/example/core_test.clj\")) 93 | (add-file (io/file \"example/src/example/core.clj\")) 94 | (dissoc :project)) 95 | => (contains-in 96 | {:registry {(str user-dir \"/example/test/example/core_test.clj\") 97 | {'example.core 98 | {'foo {:docs vector?, :meta {:added \"0.1\"}}}}, 99 | (str user-dir \"/example/src/example/core.clj\") 100 | {'example.core 101 | {'foo {:source \"(defn foo\\n [x]\\n (println x \\\"Hello, World!\\\"))\"}}}}, 102 | :references {'example.core 103 | {'foo {:docs vector?, :meta {:added \"0.1\"}, 104 | :source \"(defn foo\\n [x]\\n (println x \\\"Hello, World!\\\"))\"}}}, 105 | :namespace-lu {'example.core (str user-dir \"/example/src/example/core.clj\")}})" 106 | {:added "0.1"} 107 | [{:keys [project] :as folio} file] 108 | (let [type (file-type project file)] 109 | (println "CHANGED:" (.getName file)) 110 | (cond (#{:source :test} type) 111 | (add-code folio file type) 112 | 113 | (= :project type) 114 | (add-project folio file) 115 | 116 | (= :doc type) 117 | (add-documentation folio file) 118 | 119 | :else 120 | folio))) 121 | 122 | (defn remove-file 123 | "removes a file to the folio 124 | (-> {:project (util/read-project (io/file \"example/project.clj\"))} 125 | (add-file (io/file \"example/src/example/core.clj\")) 126 | (remove-file (io/file \"example/src/example/core.clj\")) 127 | (dissoc :project)) 128 | => {:registry {} 129 | :references {} 130 | :namespace-lu {}}" 131 | {:added "0.1"} 132 | [folio ^File file] 133 | (let [{:keys [project]} folio 134 | type (file-type project file)] 135 | (println "\nRemoving" file) 136 | 137 | (cond (#{:source :test} type) 138 | (let [fkey (.getCanonicalPath file) 139 | registry (get-in folio [:registry fkey]) 140 | diff (diff/diff {} registry) 141 | folio (-> folio 142 | (update-in [:registry] dissoc fkey) 143 | (update-in [:references] diff/patch diff))] 144 | (println "Deleting:" (-> diff :- keys)) 145 | (if (= type :source) 146 | (update-in folio [:namespace-lu] 147 | (fn [m] (reduce-kv (fn [out k v] 148 | (if (= v fkey) 149 | out 150 | (assoc out k v))) 151 | {} 152 | m))) 153 | folio)) 154 | 155 | :else folio))) 156 | -------------------------------------------------------------------------------- /src/hydrox/analyse/common.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.common) 2 | 3 | (defmulti analyse-file (fn [type file & [opts]] type)) 4 | -------------------------------------------------------------------------------- /src/hydrox/analyse/source.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.source 2 | (:require [rewrite-clj.zip :as source] 3 | [jai.query :as query] 4 | [hydrox.analyse.common :as common])) 5 | 6 | (defn analyse-source-file 7 | "analyses a source file for namespace and function definitions 8 | (analyse-source-file \"example/src/example/core.clj\" {}) 9 | => '{example.core 10 | {foo 11 | {:source \"(defn foo\\n [x]\\n (println x \\\"Hello, World!\\\"))\"}}}" 12 | {:added "0.1"} 13 | [file opts] 14 | (let [zloc (source/of-file file) 15 | nsp (-> (query/$ zloc [(ns | _ & _)] {:walk :top}) 16 | first) 17 | fns (->> (query/$ zloc [(#{defn defmulti defmacro} | _ ^:%?- string? ^:%?- map? & _)] 18 | {:return :zipper :walk :top}) 19 | (map (juxt source/sexpr 20 | (comp #(hash-map :source %) 21 | source/string 22 | source/up))) 23 | (into {}))] 24 | {nsp fns})) 25 | 26 | (defmethod common/analyse-file 27 | :source 28 | [_ file opts] 29 | (analyse-source-file file opts)) 30 | -------------------------------------------------------------------------------- /src/hydrox/analyse/test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test 2 | (:require [jai.query :as query] 3 | [rewrite-clj.zip :as source] 4 | [rewrite-clj.node :as node] 5 | [clojure.walk :as walk] 6 | [hara.data.nested :as nested] 7 | [hydrox.analyse.common :as common] 8 | [hydrox.analyse.test 9 | [common :as test] clojure midje])) 10 | 11 | (defn find-frameworks 12 | "find test frameworks given a namespace form 13 | (find-frameworks '(ns ... 14 | (:use midje.sweet))) 15 | => #{:midje} 16 | 17 | (find-frameworks '(ns ... 18 | (:use clojure.test))) 19 | => #{:clojure}" 20 | {:added "0.1"} 21 | [ns-form] 22 | (let [folio (atom #{})] 23 | (walk/postwalk (fn [form] 24 | (if-let [k (test/frameworks form)] 25 | (swap! folio conj k))) 26 | ns-form) 27 | @folio)) 28 | 29 | (defn analyse-test-file 30 | "analyses a test file for docstring forms 31 | (-> (analyse-test-file \"example/test/example/core_test.clj\" {}) 32 | (update-in '[example.core foo :docs] common/join-nodes)) 33 | => '{example.core {foo {:docs \"1\\n => 1\", :meta {:added \"0.1\"}}}}" 34 | {:added "0.1"} 35 | [file opts] 36 | (let [zloc (source/of-file file) 37 | nsloc (query/$ zloc [(ns | _ & _)] {:walk :top 38 | :return :zipper 39 | :first true}) 40 | nsp (source/sexpr nsloc) 41 | ns-form (-> nsloc source/up source/sexpr) 42 | frameworks (find-frameworks ns-form)] 43 | (->> frameworks 44 | (map (fn [framework] 45 | (test/analyse-test framework zloc))) 46 | (apply nested/merge-nested)))) 47 | 48 | (defmethod common/analyse-file 49 | :test 50 | [_ file opts] 51 | (analyse-test-file file opts)) 52 | -------------------------------------------------------------------------------- /src/hydrox/analyse/test/clojure.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test.clojure 2 | (:require [jai.query :as query] 3 | [hydrox.analyse.test.common :as common] 4 | [rewrite-clj.zip :as source] 5 | [rewrite-clj.node :as node] 6 | [clojure.string :as string])) 7 | 8 | (defn gather-is-form 9 | "Make docstring notation out of is form 10 | (common/join-nodes (gather-is-form (z/of-string \"(is (= 1 1))\"))) 11 | => \"1\\n => 1\" 12 | 13 | (common/join-nodes (gather-is-form (z/of-string \"(is (boolean? 4))\"))) 14 | => \"(boolean? 4)\\n => true\"" 15 | {:added "0.1"} 16 | ([zloc] 17 | (let [zloc (-> zloc source/down source/right)] 18 | (cond (query/match zloc '(= _ _)) 19 | (let [zloc (-> zloc source/down source/right)] 20 | [(source/node zloc) 21 | (node/newline-node "\n") 22 | (node/whitespace-node " ") 23 | (node/token-node '=> "=>") 24 | (node/whitespace-node " ") 25 | (source/node (source/right zloc))]) 26 | 27 | :else 28 | [(source/node zloc) 29 | (node/newline-node "\n") 30 | (node/whitespace-node " ") 31 | (node/token-node '=> "=>") 32 | (node/whitespace-node " ") 33 | (node/token-node true "true")])))) 34 | 35 | (defn gather-deftest-body 36 | ([zloc] 37 | (gather-deftest-body zloc [])) 38 | ([zloc output] 39 | (cond (nil? zloc) output 40 | 41 | (and (= :meta (source/tag zloc)) 42 | (= [:token :hidden] (source/value zloc))) 43 | output 44 | 45 | (query/match zloc string?) 46 | (recur (source/right* zloc) 47 | (conj output (common/gather-string zloc))) 48 | 49 | (query/match zloc 'is) 50 | (recur (source/right* zloc) (vec (concat output (gather-is-form zloc)))) 51 | 52 | :else 53 | (recur (source/right* zloc) (conj output (source/node zloc)))))) 54 | 55 | (defn gather-deftest 56 | "Make docstring notation out of deftest form 57 | (-> \"^{:refer example/hello-world :added \\\"0.1\\\"} 58 | (deftest hello-world-test\\n \\\"Sample test program\\\"\\n (is (= 1 1))\\n (is (identical? 2 4)))\" 59 | (z/of-string) 60 | z/down z/right z/down z/right z/right 61 | (gather-deftest) 62 | :docs 63 | common/join-nodes) 64 | => \"\"Sample test program\"\\n 1\\n => 1\\n (identical? 2 4)\\n => true\"" 65 | {:added "0.1"} 66 | [zloc] 67 | (if-let [mta (common/gather-meta zloc)] 68 | (assoc mta :docs (gather-deftest-body zloc)))) 69 | 70 | (defmethod common/frameworks 'clojure.test [_] :clojure) 71 | 72 | (defmethod common/analyse-test :clojure 73 | [type zloc] 74 | (let [fns (query/$ zloc [(deftest _ | & _)] {:return :zipper :walk :top})] 75 | (->> (keep gather-deftest fns) 76 | (reduce (fn [m {:keys [ns var docs] :as meta}] 77 | (-> m 78 | (assoc-in [ns var :docs] docs) 79 | (assoc-in [ns var :meta] (dissoc meta :docs :ns :var :refer)))) 80 | {})))) 81 | -------------------------------------------------------------------------------- /src/hydrox/analyse/test/common.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test.common 2 | (:require [rewrite-clj.zip :as source] 3 | [rewrite-clj.node :as node] 4 | [clojure.string :as string])) 5 | 6 | (defmulti frameworks (fn [sym] sym)) 7 | (defmethod frameworks :default [_]) 8 | 9 | (defmulti analyse-test (fn [type zloc] type)) 10 | 11 | (defn gather-meta 12 | "gets the metadata for a particular form 13 | (-> (z/of-string \"^{:refer clojure.core/+ :added \\\"0.1\\\"}\\n(fact ...)\") 14 | z/down z/right z/down 15 | gather-meta) 16 | => '{:added \"0.1\", :ns clojure.core, :var +, :refer clojure.core/+}" 17 | {:added "0.1"} 18 | [zloc] 19 | (if (-> zloc source/up source/up source/tag (= :meta)) 20 | (let [mta (-> zloc source/up source/left source/sexpr) 21 | sym (:refer mta)] 22 | (if sym 23 | (assoc mta 24 | :ns (symbol (str (.getNamespace sym))) 25 | :var (symbol (name sym))))))) 26 | 27 | 28 | (defn gather-string 29 | "creates correctly spaced code string from normal docstring 30 | 31 | (-> (z/of-string \"\\\"hello\\nworld\\nalready\\\"\") 32 | (gather-string) 33 | (str)) 34 | => \"\"hello\\n world\\n already\"\"" 35 | {:added "0.1"} 36 | [zloc] 37 | (node/string-node (->> (source/sexpr zloc) 38 | (string/split-lines) 39 | (map-indexed (fn [i s] 40 | (str (if (zero? i) "" " ") 41 | (string/triml s))) )))) 42 | 43 | (defn strip-quotes 44 | "takes away the quotes from a string for formatting purposes 45 | 46 | (strip-quotes \"\\\"hello\\\"\") 47 | => \"hello\"" 48 | {:added "0.1"} 49 | [s] 50 | (if (and (.startsWith s "\"") 51 | (.endsWith s "\"")) 52 | (subs s 1 (dec (count s))) 53 | s)) 54 | 55 | (defn join-nodes [nodes] 56 | (->> nodes 57 | (map node/string) 58 | (string/join))) 59 | -------------------------------------------------------------------------------- /src/hydrox/analyse/test/midje.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test.midje 2 | (:require [jai.query :as query] 3 | [clojure.string :as string] 4 | [rewrite-clj.zip :as source] 5 | [rewrite-clj.node :as node] 6 | [hydrox.analyse.test.common :as common])) 7 | 8 | (defn gather-fact-body 9 | ([zloc] 10 | (gather-fact-body zloc [])) 11 | ([zloc output] 12 | (cond (nil? zloc) output 13 | 14 | (and (= :meta (source/tag zloc)) 15 | (= [:token :hidden] (source/value zloc))) 16 | output 17 | 18 | (query/match zloc string?) 19 | (recur (source/right* zloc) 20 | (conj output (common/gather-string zloc))) 21 | 22 | :else 23 | (recur (source/right* zloc) (conj output (source/node zloc)))))) 24 | 25 | (defn gather-fact 26 | "Make docstring notation out of fact form 27 | (-> \"^{:refer example/hello-world :added \\\"0.1\\\"} 28 | (fact \\\"Sample test program\\\"\\n (+ 1 1) => 2\\n (long? 3) => true)\" 29 | (z/of-string) 30 | z/down z/right z/down z/right 31 | (gather-fact) 32 | :docs 33 | common/join-nodes) 34 | => \"\"Sample test program\"\\n (+ 1 1) => 2\\n (long? 3) => true\"" 35 | {:added "0.1"} 36 | [zloc] 37 | (if-let [mta (common/gather-meta zloc)] 38 | (assoc mta :docs (gather-fact-body zloc)))) 39 | 40 | (defmethod common/frameworks 'midje.sweet [_] :midje) 41 | 42 | (defmethod common/analyse-test :midje 43 | ([type zloc] 44 | (let [fns (query/$ zloc [(fact | & _)] {:return :zipper :walk :top})] 45 | (->> (keep gather-fact fns) 46 | (reduce (fn [m {:keys [ns var docs] :as meta}] 47 | (-> m 48 | (assoc-in [ns var :docs] docs) 49 | (assoc-in [ns var :meta] (dissoc meta :docs :ns :var :refer)))) 50 | {}))))) 51 | -------------------------------------------------------------------------------- /src/hydrox/common/data.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.common.data) 2 | 3 | ;; {:source { { { <>}}} 4 | ;; :test { { { <>}}} 5 | ;; :namespace { }} 6 | 7 | (defrecord Registry [] 8 | Object 9 | (toString [obj] 10 | (str "#registry" (->> obj (into {}))))) 11 | 12 | (defmethod print-method Registry 13 | [v w] 14 | (.write w (str v))) 15 | 16 | (defn registry 17 | ([] (Registry.)) 18 | ([m] (map->Registry m))) 19 | 20 | ;; {:project <> :articles <> :reference <> :template <> :registry <>} 21 | 22 | (defrecord Folio [] 23 | Object 24 | (toString [obj] 25 | (str "#folio" (-> obj keys vec)))) 26 | 27 | (defmethod print-method Folio 28 | [v w] 29 | (.write w (str v))) 30 | 31 | (defn folio 32 | "constructs a folio object" 33 | {:added "0.1"} 34 | ([m] (map->Folio m))) 35 | 36 | ;; { { {:source <> :docs <> :meta <>}}} 37 | 38 | (defrecord References [] 39 | Object 40 | (toString [obj] 41 | (str "#references" 42 | (reduce-kv (fn [nsm nsk vars] 43 | (assoc nsm nsk 44 | (reduce-kv (fn [vm vk vs] 45 | (assoc vm vk (-> vs keys sort vec))) 46 | {} 47 | vars))) 48 | {} 49 | obj)))) 50 | 51 | (defmethod print-method References 52 | [v w] 53 | (.write w (str v))) 54 | 55 | (defn references 56 | "constructs a reference object" 57 | {:added "0.1"} 58 | ([] (References.)) 59 | ([m] (map->References m))) 60 | -------------------------------------------------------------------------------- /src/hydrox/common/util.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.common.util 2 | (:require [clojure.java.io :as io])) 3 | 4 | (defn full-path 5 | "constructs a path from a project 6 | 7 | (full-path \"example/file.clj\" \"src\" {:root \"/home/user\"}) 8 | => \"/home/user/src/example/file.clj\"" 9 | {:added "0.1"} [path base project] 10 | (str (:root project) "/" base "/" path)) 11 | 12 | (defn filter-pred 13 | "filters values of a map that fits the predicate 14 | (filter-pred string? {:a \"valid\" :b 0}) 15 | => {:a \"valid\"}" 16 | {:added "0.1"} [pred? m] 17 | (reduce-kv (fn [m k v] (if (pred? v) 18 | (assoc m k v) 19 | m)) 20 | {} m)) 21 | 22 | (defn escape-dollars 23 | "for regex purposes, escape dollar signs in templates" 24 | {:added "0.1"} 25 | [s] 26 | (.replaceAll s "\\$" "\\\\\\$")) 27 | 28 | (defn read-project 29 | "like `leiningen.core.project/read` but with less features' 30 | 31 | (keys (read-project (io/file \"example/project.clj\"))) 32 | => (just [:description :license :name :source-paths :test-paths 33 | :documentation :root :url :version :dependencies] :in-any-order)" 34 | {:added "0.1"} 35 | ([] (read-project (io/file "project.clj"))) 36 | ([file] 37 | (let [path (.getCanonicalPath file) 38 | root (subs path 0 (- (count path) 12)) 39 | pform (read-string (slurp file)) 40 | [_ name version] (take 3 pform) 41 | proj (->> (drop 3 pform) 42 | (concat [:name name 43 | :version version 44 | :root root]) 45 | (apply hash-map))] 46 | (-> proj 47 | (update-in [:source-paths] (fnil identity ["src"])) 48 | (update-in [:test-paths] (fnil identity ["test"])))))) 49 | -------------------------------------------------------------------------------- /src/hydrox/core.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.core 2 | (:require [hydrox.meta :as meta] 3 | [hydrox.doc :as doc] 4 | [hydrox.core.regulator :as regulator] 5 | [hydrox.core.patch :as patch] 6 | [hydrox.common.util :as util] 7 | [hara.component :as component] 8 | [clojure.java.io :as io])) 9 | 10 | (defn submerged? 11 | "checks if dive has started" 12 | {:added "0.1"} 13 | ([] (submerged? regulator/*running*)) 14 | ([regs] 15 | (if (empty? regs) false true))) 16 | 17 | (defn single-use 18 | "returns a working regulator for a given project file" 19 | {:added "0.1"} 20 | ([] (single-use "project.clj")) 21 | ([path] 22 | (patch/patch-read-keyword) 23 | (let [proj (util/read-project (io/file path)) 24 | folio (-> proj 25 | (regulator/create-folio) 26 | (regulator/init-folio)) 27 | state (atom folio)] 28 | (regulator/regulator state proj)))) 29 | 30 | (defn import-docstring 31 | "imports docstrings given a regulator" 32 | {:added "0.1"} 33 | ([] 34 | (if (submerged?) 35 | (mapv #(import-docstring % :all) regulator/*running*) 36 | (println "call `dive` first before running this function"))) 37 | ([reg] (import-docstring reg :all)) 38 | ([reg ns] (import-docstring reg ns nil)) 39 | ([{:keys [state project] :as reg} ns var] 40 | (let [{:keys [references] 41 | lu :namespace-lu} @state] 42 | (cond (= ns :all) 43 | (doall (meta/import-project project references)) 44 | 45 | :else 46 | (if-let [file (get lu ns)] 47 | (if var 48 | (meta/import-var file var references) 49 | (meta/import-file file references))))))) 50 | 51 | (defn purge-docstring 52 | "purges docstrings given a regulator" 53 | {:added "0.1"} 54 | ([] 55 | (if (submerged?) 56 | (mapv #(purge-docstring % :all) regulator/*running*) 57 | (println "call `dive` first before running this function"))) 58 | ([reg] (purge-docstring reg :all)) 59 | ([reg ns] (purge-docstring reg ns nil)) 60 | ([{:keys [state project] :as reg} ns var] 61 | (let [{lu :namespace-lu} @state] 62 | (cond (= ns :all) 63 | (doall (meta/purge-project project)) 64 | 65 | :else 66 | (if-let [file (get lu ns)] 67 | (if var 68 | (meta/purge-var file var) 69 | (meta/purge-file file))))))) 70 | 71 | (defn generate-docs 72 | "generates html docs for :documentation entries in project.clj" 73 | {:added "0.1"} 74 | ([] 75 | (if (submerged?) 76 | (mapv generate-docs regulator/*running*) 77 | (println "call `dive` first before running this function"))) 78 | ([{:keys [state] :as reg}] 79 | (doc/render-all @state)) 80 | ([{:keys [state] :as reg} name] 81 | (doc/render-single @state name))) 82 | 83 | (defn dive 84 | "starts a dive" 85 | {:added "0.1"} 86 | ([] (dive "project.clj")) 87 | ([path] (dive path {})) 88 | ([path opts] 89 | (patch/patch-read-keyword) 90 | (->> (io/file path) 91 | (util/read-project) 92 | (merge opts) 93 | (regulator/regulator) 94 | (component/start)))) 95 | 96 | (defn surface 97 | "finishes a dive" 98 | {:added "0.1"} 99 | ([] (doseq [reg regulator/*running*] 100 | (surface reg))) 101 | ([regulator] 102 | (component/stop regulator))) 103 | -------------------------------------------------------------------------------- /src/hydrox/core/patch.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.core.patch 2 | (:require [clojure.tools.reader.edn :as edn] 3 | [clojure.tools.reader.impl.commons :as common] 4 | [clojure.tools.reader.impl.utils :as utils] 5 | [clojure.tools.reader.reader-types :as reader])) 6 | 7 | (defn read-keyword 8 | [reader initch opts] 9 | (let [ch (reader/read-char reader)] 10 | (if-not (utils/whitespace? ch) 11 | (let [token (#'edn/read-token reader ch)] 12 | (keyword token)) 13 | (reader/reader-error reader "Invalid token: :")))) 14 | 15 | (defn patch-read-keyword [] 16 | (alter-var-root #'clojure.tools.reader.edn/read-keyword 17 | (fn [_] 18 | read-keyword))) 19 | -------------------------------------------------------------------------------- /src/hydrox/core/regulator.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.core.regulator 2 | (:require [hydrox.meta.util :as util] 3 | [hydrox.meta :as meta] 4 | [hydrox.analyse :as analyser] 5 | [hydrox.common.data :as data] 6 | [hara.common.watch :as watch] 7 | [hara.event :as event] 8 | [hara.component :as component] 9 | [hara.io.watch] 10 | [hara.concurrent.pipe :as pipe] 11 | [clojure.java.io :as io])) 12 | 13 | (defonce ^:dynamic *running* #{}) 14 | 15 | (defn create-folio 16 | "creates the folio for storing all the documentation information" 17 | {:added "0.1"} 18 | [{:keys [root initialise filter manual] :as project}] 19 | (data/folio {:meta {} 20 | :articles {} 21 | :namespaces {} 22 | :project project 23 | :references (data/references) 24 | :registry (data/registry) 25 | :root root 26 | :initialise (if-not (nil? initialise) initialise true) 27 | :filter (or filter [".clj" ".cljs" ".cljc"]) 28 | :manual (or manual false) 29 | :pipe (atom nil)})) 30 | 31 | (defn mount-folio 32 | "adds a watcher to update function/test definitions when files in the project changes" 33 | {:added "0.1"} 34 | [state {:keys [project root channel filter pipe] :as folio}] 35 | (let [{:keys [source-paths test-paths]} project] 36 | (watch/add (io/as-file root) :hydrox 37 | (fn [_ _ _ [type file :as out]] 38 | (pipe/send @pipe out)) 39 | {:filter filter 40 | :recursive true 41 | :include (concat source-paths test-paths)}) 42 | folio)) 43 | 44 | (defn unmount-folio 45 | "removes the file-change watcher" 46 | {:added "0.1"} 47 | [folio] 48 | (watch/remove (io/as-file (:root folio)) :hydrox)) 49 | 50 | (defn init-folio 51 | "runs through all the files and adds function/test definitions to the project" 52 | {:added "0.1"} 53 | [{:keys [project initialise] :as folio}] 54 | (if initialise 55 | (-> folio 56 | (assoc :initialisation true) 57 | (#(reduce (fn [folio file] 58 | (analyser/add-file folio file)) 59 | % 60 | (concat (util/all-files project :source-paths ".clj") 61 | (util/all-files project :test-paths ".clj")))) 62 | (dissoc :initialisation)) 63 | folio)) 64 | 65 | (defn init-pipe 66 | [state] 67 | (reset! (:pipe @state) 68 | (pipe/pipe (fn [[type file]] 69 | (case type 70 | :create (swap! state analyser/add-file file) 71 | :modify (swap! state analyser/add-file file) 72 | :delete (swap! state analyser/remove-file file)))))) 73 | 74 | (defn start-regulator 75 | "starts the regulator" 76 | {:added "0.1"} 77 | [{:keys [project state] :as obj}] 78 | (let [folio (-> (create-folio project) 79 | (init-folio))] 80 | (mount-folio state folio) 81 | (reset! state folio) 82 | (init-pipe state) 83 | (event/signal [:log {:msg (str "Regulator for " (:name project) " started.")}]) 84 | (alter-var-root #'*running* (fn [s] (conj s obj))) 85 | obj)) 86 | 87 | (defn stop-regulator 88 | "stops the regulator" 89 | {:added "0.1"} 90 | [{:keys [project state] :as obj}] 91 | (unmount-folio @state) 92 | (reset! state nil) 93 | (event/signal [:log {:msg (str "Regulator for " (:name project) " stopped.")}]) 94 | (alter-var-root #'*running* (fn [s] (disj s obj))) 95 | obj) 96 | 97 | 98 | (defrecord Regulator [state project] 99 | 100 | component/IComponent 101 | (-start [obj] (start-regulator obj)) 102 | (-stop [obj] (stop-regulator obj)) 103 | 104 | (-stopped? [obj] 105 | (nil? @state))) 106 | 107 | (defn regulator 108 | "creates a blank regulator, does not work" 109 | {:added "0.1"} 110 | ([project] 111 | (Regulator. (atom nil) project)) 112 | ([state project] 113 | (Regulator. state project))) 114 | -------------------------------------------------------------------------------- /src/hydrox/doc.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc 2 | (:require [rewrite-clj.zip :as source] 3 | [jai.query :as query] 4 | [me.raynes.fs :as fs] 5 | [hydrox.common.util :as util] 6 | [hydrox.doc 7 | [collect :as collect] 8 | [link :as link] 9 | [parse :as parse] 10 | [render :as render] 11 | [structure :as structure]])) 12 | 13 | (defn prepare-article 14 | "generates the flat outline for rendering" 15 | {:added "0.1"} 16 | [folio name file] 17 | (let [elements (parse/parse-file file folio)] 18 | (-> (assoc-in folio [:articles name :elements] elements) 19 | (collect/collect-global name) 20 | (collect/collect-article name) 21 | (collect/collect-namespaces name) 22 | (collect/collect-tags name) 23 | (collect/collect-citations name) 24 | (link/link-namespaces name) 25 | (link/link-references name) 26 | (link/link-numbers name) 27 | (link/link-tags name) 28 | (link/link-anchors-lu name) 29 | (link/link-anchors name) 30 | (link/link-stencil name)))) 31 | 32 | (defn generate 33 | "generates the tree outline for rendering" 34 | {:added "0.1"} 35 | [{:keys [project] :as folio} name] 36 | (let [meta (-> project :documentation :files (get name)) 37 | folio (prepare-article folio name (:input meta)) 38 | elements (get-in folio [:articles name :elements]) 39 | structure (structure/structure elements)] 40 | structure)) 41 | 42 | (defn find-includes 43 | "finds elements with `@=` tags 44 | 45 | (find-includes \"<@=hello> <@=world>\") 46 | => #{:hello :world}" 47 | {:added "0.1"} 48 | [html] 49 | (->> html 50 | (re-seq #"<@=([^>^<]+)>") 51 | (map second) 52 | (map keyword) 53 | set)) 54 | 55 | (defn prepare-includes 56 | "prepare template accept includes" 57 | {:added "0.1"} 58 | [name includes folio] 59 | (let [no-doc (->> (filter (fn [[k v]] (#{:article :navigation} v)) includes) 60 | empty?)] 61 | (cond no-doc 62 | includes 63 | 64 | :else 65 | (let [elements (generate folio name)] 66 | (reduce-kv (fn [out k v] 67 | (assoc out k (case v 68 | :article (render/render-article elements folio) 69 | :navigation (render/render-navigation elements folio) 70 | v))) 71 | {} 72 | includes))))) 73 | 74 | (defn render-entry 75 | "helper function that is called by both render-single and render-all" 76 | {:added "0.1"} 77 | [name entry folio] 78 | (println "Rendering" name "....") 79 | (try 80 | (let [project (:project folio) 81 | opts (:documentation project) 82 | entry (merge (util/filter-pred string? project) 83 | (-> opts :template :defaults) 84 | entry) 85 | template-path (util/full-path (:template entry) (-> opts :template :path) project) 86 | output-path (util/full-path (str name ".html") (:output opts) project) 87 | template (slurp template-path) 88 | includes (->> (find-includes template) 89 | (select-keys entry)) 90 | includes (prepare-includes name includes folio) 91 | html (render/replace-template template includes opts project)] 92 | (spit output-path html) 93 | (println "SUCCESS")) 94 | (catch Throwable t 95 | (println "ERROR") 96 | (.printStackTrace t) 97 | (println "Unable to render" name)))) 98 | 99 | (defn copy-files 100 | "copies all files from the template directory into the output directory" 101 | {:added "0.1"} 102 | [folio] 103 | (let [root (-> folio :project :root) 104 | path (-> folio :project :documentation :template :path) 105 | target (str root "/" (-> folio :project :documentation :output)) 106 | sources (-> folio :project :documentation :template :copy)] 107 | (mapv #(fs/copy-dir-into (str root "/" path "/" %) target) sources))) 108 | 109 | (defn render-single 110 | "render for a single entry in the project.clj map" 111 | {:added "0.1"} 112 | [folio name] 113 | (let [opts (-> folio :project :documentation) 114 | entry (get-in opts [:files name])] 115 | (copy-files folio) 116 | (render-entry name entry folio))) 117 | 118 | (defn render-all 119 | "render for all documentation entries in the project.clj map" 120 | {:added "0.1"} 121 | [folio] 122 | (let [opts (-> folio :project :documentation)] 123 | (copy-files folio) 124 | (println "DOCS:" (keys (:files opts))) 125 | (doseq [[name entry] (:files opts)] 126 | (render-entry name entry folio)))) 127 | -------------------------------------------------------------------------------- /src/hydrox/doc/checks.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.checks 2 | (:require [jai.query :as query] 3 | [rewrite-clj.zip :as source] 4 | [rewrite-clj.node :as node])) 5 | 6 | (def directives 7 | #{:article :file :reference :ns 8 | :appendix :chapter 9 | :section :subsection :subsubsection 10 | :image :paragraph :code 11 | :equation :citation}) 12 | 13 | (defn wrap-meta [f] 14 | (fn [zloc selector] 15 | (if (= :meta (source/tag zloc)) 16 | (f (-> zloc source/down source/right) selector) 17 | (f zloc selector)))) 18 | 19 | (defn directive? 20 | "checks if the element is a directive. 21 | 22 | (-> \"[[:chapter {:title \\\"A Story\\\"}]]\" 23 | z/of-string 24 | directive?) 25 | => true" 26 | {:added "0.1"} 27 | ([zloc] 28 | ((wrap-meta query/match) zloc {:pattern [[#'keyword? #'map?]]})) 29 | ([zloc kw] 30 | ((wrap-meta query/match) zloc {:pattern [[kw #'map?]]}))) 31 | 32 | (defn attribute? 33 | "checks if the element is an attribute. 34 | 35 | (-> \"[[{:title \\\"A Story\\\"}]]\" 36 | z/of-string 37 | attribute?) 38 | => true" 39 | {:added "0.1"} 40 | [zloc] 41 | ((wrap-meta query/match) zloc {:pattern [[#'map?]]})) 42 | 43 | (defn code-directive? 44 | "checks if the element is a code directive 45 | 46 | (-> \"[[:code {:type :javascript} 47 | \\\"1 + 1 == 2\\\"]]\" 48 | z/of-string 49 | code-directive?) 50 | => true" 51 | {:added "0.1"} 52 | [zloc] 53 | ((wrap-meta query/match) zloc {:pattern [[:code #'map? #'string?]]})) 54 | 55 | (defn ns? 56 | "checks if the element is a ns form 57 | 58 | (-> \"(ns ...)\" 59 | z/of-string 60 | ns?) 61 | => true" 62 | {:added "0.1"} 63 | [zloc] 64 | ((wrap-meta query/match) zloc {:form 'ns})) 65 | 66 | (defn fact? 67 | "checks if the element is a fact form 68 | 69 | (-> \"(fact ...)\" 70 | z/of-string 71 | fact?) 72 | => true" 73 | {:added "0.1"} 74 | [zloc] 75 | ((wrap-meta query/match) zloc {:form 'fact})) 76 | 77 | (defn facts? 78 | "checks if the element is a facts form 79 | 80 | (-> \"(facts ...)\" 81 | z/of-string 82 | facts?) 83 | => true" 84 | {:added "0.1"} 85 | [zloc] 86 | ((wrap-meta query/match) zloc {:form 'facts})) 87 | 88 | (defn comment? 89 | "checks if the element is a comment form" 90 | {:added "0.1"} 91 | [zloc] 92 | ((wrap-meta query/match) zloc {:form 'comment})) 93 | 94 | (defn deftest? 95 | [zloc] 96 | ((wrap-meta query/match) zloc {:form 'deftest})) 97 | 98 | (defn is? 99 | [zloc] 100 | ((wrap-meta query/match) zloc {:form 'is})) 101 | 102 | (defn paragraph? 103 | "checks if the element is a paragraph (string)" 104 | {:added "0.1"} 105 | [zloc] 106 | (string? (source/sexpr zloc))) 107 | 108 | (defn whitespace? 109 | "checks if the element is a whitespace element" 110 | {:added "0.1"} 111 | [zloc] 112 | (node/whitespace-or-comment? (source/node zloc))) 113 | -------------------------------------------------------------------------------- /src/hydrox/doc/collect.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.collect 2 | (:require [hara.data.nested :as nested])) 3 | 4 | (defn collect-namespaces 5 | "combines `:ns-form` directives into a namespace map for easy referral 6 | 7 | (collect-namespaces 8 | {:articles {\"example\" {:elements [{:type :ns-form 9 | :ns 'clojure.core}]}}} 10 | \"example\") 11 | => '{:articles {\"example\" {:elements ()}} 12 | :namespaces {clojure.core {:type :ns-form :ns clojure.core}}}" 13 | {:added "0.1"} 14 | [{:keys [articles] :as folio} name] 15 | (let [namespaces (->> (get-in articles [name :elements]) 16 | (filter #(-> % :type (= :ns-form))) 17 | (map (juxt :ns identity)) 18 | (into {}))] 19 | (-> folio 20 | (update-in [:namespaces] (fnil nested/merge-nested {}) namespaces) 21 | (update-in [:articles name :elements] 22 | (fn [elements] (filter #(-> % :type (not= :ns-form)) elements)))))) 23 | 24 | (defn collect-article 25 | "shunts `:article` directives into a seperate `:meta` section 26 | 27 | (collect-article 28 | {:articles {\"example\" {:elements [{:type :article 29 | :options {:color :light}}]}}} 30 | \"example\") 31 | => '{:articles {\"example\" {:elements [] 32 | :meta {:options {:color :light}}}}}" 33 | {:added "0.1"} 34 | [{:keys [articles] :as folio} name] 35 | (let [articles (->> (get-in articles [name :elements]) 36 | (filter #(-> % :type (= :article))) 37 | (apply nested/merge-nested {}))] 38 | (-> folio 39 | (update-in [:articles name :meta] (fnil nested/merge-nested {}) (dissoc articles :type)) 40 | (update-in [:articles name :elements] 41 | (fn [elements] (filter #(-> % :type (not= :article)) elements)))))) 42 | 43 | (defn collect-global 44 | "shunts `:global` directives into a globally available `:meta` section 45 | 46 | (collect-global 47 | {:articles {\"example\" {:elements [{:type :global 48 | :options {:color :light}}]}}} 49 | \"example\") 50 | => {:articles {\"example\" {:elements ()}} 51 | :meta {:options {:color :light}}}" 52 | {:added "0.1"} 53 | [{:keys [articles] :as folio} name] 54 | (let [global (->> (get-in articles [name :elements]) 55 | (filter #(-> % :type (= :global))) 56 | (apply nested/merge-nested {}))] 57 | (-> folio 58 | (update-in [:meta] (fnil nested/merge-nested {}) (dissoc global :type)) 59 | (update-in [:articles name :elements] 60 | (fn [elements] (filter #(-> % :type (not= :global)) elements)))))) 61 | 62 | (defn collect-tags 63 | "puts any element with `:tag` attribute into a seperate `:tag` set 64 | 65 | (collect-tags 66 | {:articles {\"example\" {:elements [{:type :chapter :tag \"hello\"} 67 | {:type :chapter :tag \"world\"}]}}} 68 | \"example\") 69 | => {:articles {\"example\" {:elements [{:type :chapter :tag \"hello\"} 70 | {:type :chapter :tag \"world\"}] 71 | :tags #{\"hello\" \"world\"}}}}" 72 | {:added "0.1"} 73 | [{:keys [articles] :as folio} name] 74 | (->> (get-in articles [name :elements]) 75 | (reduce (fn [m {:keys [tag] :as ele}] 76 | (cond (nil? tag) m 77 | 78 | (get m tag) (do (println "There is already an existing tag for" ele) 79 | m) 80 | :else (conj m tag))) 81 | #{}) 82 | (assoc-in folio [:articles name :tags]))) 83 | 84 | (defn collect-citations 85 | "shunts `:citation` directives into a seperate `:citation` section 86 | 87 | (collect-citations 88 | {:articles {\"example\" {:elements [{:type :citation :author \"Chris\"}]}}} 89 | \"example\") 90 | => {:articles {\"example\" {:elements [], 91 | :citations [{:type :citation, :author \"Chris\"}]}}}" 92 | {:added "0.1"} 93 | [{:keys [articles] :as folio} name] 94 | (let [citations (->> (get-in articles [name :elements]) 95 | (filter #(-> % :type (= :citation))))] 96 | (-> folio 97 | (assoc-in [:articles name :citations] citations) 98 | (update-in [:articles name :elements] 99 | (fn [elements] (filter #(-> % :type (not= :citation)) elements)))))) 100 | -------------------------------------------------------------------------------- /src/hydrox/doc/link.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link 2 | (:require [hara.namespace.import :as ns] 3 | [hydrox.doc.link 4 | anchors 5 | namespaces 6 | numbers 7 | references 8 | stencil 9 | tags])) 10 | 11 | (ns/import hydrox.doc.link.anchors [link-anchors link-anchors-lu] 12 | hydrox.doc.link.namespaces [link-namespaces] 13 | hydrox.doc.link.numbers [link-numbers] 14 | hydrox.doc.link.references [link-references] 15 | hydrox.doc.link.stencil [link-stencil] 16 | hydrox.doc.link.tags [link-tags]) 17 | -------------------------------------------------------------------------------- /src/hydrox/doc/link/anchors.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.anchors) 2 | 3 | (defn link-anchors-lu 4 | "creates the anchor lookup for tags and numbers 5 | (-> {:articles {\"example\" {:elements [{:type :chapter :tag \"hello\" :number \"1\"}]}}} 6 | (link-anchors-lu \"example\") 7 | :anchors-lu) 8 | => {\"example\" {:by-number {:chapter {\"1\" {:type :chapter, :tag \"hello\", :number \"1\"}}}, 9 | :by-tag {\"hello\" {:type :chapter, :tag \"hello\", :number \"1\"}}}}" 10 | {:added "0.1"} 11 | [{:keys [articles] :as folio} name] 12 | (let [anchors (->> (get-in articles [name :elements]) 13 | (filter :tag) 14 | (map #(select-keys % [:type :tag :number])))] 15 | 16 | (->> anchors 17 | (reduce (fn [m {:keys [type tag number] :as anchor}] 18 | (let [m (if number 19 | (assoc-in m [:by-number type number] anchor) 20 | m)] 21 | (assoc-in m [:by-tag tag] anchor))) 22 | {}) 23 | (assoc-in folio [:anchors-lu name])))) 24 | 25 | (defn link-anchors 26 | "creates a global anchors list based on the lookup 27 | 28 | (-> {:articles {\"example\" {:elements [{:type :chapter :tag \"hello\" :number \"1\"}]}}} 29 | (link-anchors-lu \"example\") 30 | (link-anchors \"example\") 31 | :anchors) 32 | => {\"example\" {\"hello\" {:type :chapter, :tag \"hello\", :number \"1\"}}}" 33 | {:added "0.1"} 34 | [{:keys [anchors-lu articles] :as folio} name] 35 | (->> (get anchors-lu name) 36 | :by-tag 37 | (assoc-in folio [:anchors name]))) 38 | -------------------------------------------------------------------------------- /src/hydrox/doc/link/namespaces.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.namespaces) 2 | 3 | (defn link-namespaces 4 | "link elements with `:ns` forms to code 5 | 6 | (link-namespaces 7 | {:articles {\"example\" {:elements [{:type :ns :ns \"clojure.core\"}]}} 8 | :namespaces {\"clojure.core\" {:code \"(ns clojure.core)\"}}} 9 | \"example\") 10 | => {:articles {\"example\" {:elements [{:type :code 11 | :ns \"clojure.core\" 12 | :origin :ns 13 | :indentation 0 14 | :code \"(ns clojure.core)\"}]}} 15 | :namespaces {\"clojure.core\" {:code \"(ns clojure.core)\"}}}" 16 | {:added "0.1"} 17 | [{:keys [namespaces articles] :as folio} name] 18 | (update-in folio [:articles name :elements] 19 | (fn [elements] 20 | (mapv (fn [element] 21 | (if (= :ns (:type element)) 22 | (let [{:keys [ns code]} element] 23 | (assoc element 24 | :type :code 25 | :origin :ns 26 | :indentation 0 27 | :code (get-in namespaces [ns :code]))) 28 | element)) 29 | elements)))) 30 | -------------------------------------------------------------------------------- /src/hydrox/doc/link/numbers.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.numbers) 2 | 3 | (def new-counter 4 | {:chapter 0 5 | :section 0 6 | :subsection 0 7 | :subsubsection 0 8 | :code 0 9 | :image 0 10 | :equation 0 11 | :citation 0}) 12 | 13 | (defn increment 14 | "increments a string for alphanumerics and numbers 15 | (increment \"1\") 16 | => \"2\" 17 | 18 | (increment \"A\") 19 | => \"B\"" 20 | {:added "0.1"} 21 | [count] 22 | (if (number? count) 23 | "A" 24 | (->> count 25 | first 26 | char 27 | int inc char str))) 28 | 29 | (defn link-numbers-loop 30 | "main loop logic for generation of numbers" 31 | {:added "0.1"} 32 | ([elements auto-number] 33 | (link-numbers-loop elements auto-number new-counter [])) 34 | ([[{:keys [type origin] :as ele} & more :as elements] 35 | auto-number 36 | {:keys [chapter section subsection subsubsection code image equation citation] :as counter} 37 | output] 38 | (if (empty? elements) 39 | output 40 | (let [[numstring counter] 41 | (case type 42 | :citation 43 | [(str (inc citation)) 44 | (assoc counter :citation (inc citation))] 45 | 46 | :chapter 47 | [(str (inc chapter)) 48 | (assoc counter 49 | :chapter (if (number? chapter) 50 | (inc chapter) 51 | 0) 52 | :section 0 :subsection 0 :subsubsection 0 :code 0)] 53 | :section 54 | [(str chapter "." (inc section)) 55 | (assoc counter :section (inc section) :subsection 0 :subsubsection 0)] 56 | 57 | :subsection 58 | [(str chapter "." section "." (inc subsection)) 59 | (assoc counter :subsection (inc subsection) :subsubsection 0)] 60 | 61 | :subsubsection 62 | [(str chapter "." section "." subsection "." (inc subsubsection)) 63 | (assoc counter :subsubsection (inc subsubsection))] 64 | 65 | :appendix 66 | [(str (increment chapter)) 67 | (assoc counter 68 | :chapter (increment chapter) 69 | :section 0 :subsection 0 :subsubsection 0 :code 0)] 70 | 71 | (if (and (#{:code :image :equation :block} type) 72 | (or (auto-number type) 73 | (auto-number origin) 74 | (:numbered ele)) 75 | (or (:tag ele) (:title ele)) 76 | (not (or (:hidden ele) 77 | (false? (:numbered ele))))) 78 | (case type 79 | 80 | :code 81 | [(str chapter "." (inc code)) 82 | (assoc counter :code (inc code))] 83 | 84 | :block 85 | [(str chapter "." (inc code)) 86 | (assoc counter :code (inc code))] 87 | 88 | :image 89 | [(str (inc image)) 90 | (assoc counter :image (inc image))] 91 | 92 | :equation 93 | [(str (inc equation)) 94 | (assoc counter :equation (inc equation))]) 95 | 96 | [nil counter])) 97 | ele (if numstring 98 | (assoc ele :number numstring) 99 | ele)] 100 | (recur more auto-number counter (conj output ele)))))) 101 | 102 | (defn link-numbers 103 | "creates numbers for main sections, images, code and equations 104 | (link-numbers {:articles {\"example\" {:elements [{:type :chapter :title \"hello\"} 105 | {:type :section :title \"world\"}]}}} 106 | \"example\") 107 | {:articles {\"example\" 108 | {:elements [{:type :chapter, :title \"hello\", :number \"1\"} 109 | {:type :section, :title \"world\", :number \"1.1\"}]}}}" 110 | {:added "0.1"} 111 | [folio name] 112 | (update-in folio [:articles name :elements] 113 | (fn [elements] 114 | (let [auto-number (->> (list (get-in folio [:articles name :meta :link :auto-number]) 115 | (get-in folio [:meta :link :auto-number]) 116 | true) 117 | (drop-while nil?) 118 | (first)) 119 | auto-number (cond (set? auto-number) auto-number 120 | (false? auto-number) #{} 121 | (true? auto-number) #{:image :equation :code :block})] 122 | (link-numbers-loop elements auto-number))))) 123 | -------------------------------------------------------------------------------- /src/hydrox/doc/link/references.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.references 2 | (:require [hara.data.nested :as nested] 3 | [hydrox.meta.util :as util] 4 | [rewrite-clj.node :as node] 5 | [clojure.string :as string])) 6 | 7 | (defn process-doc-nodes 8 | "treat test nodes specially when rendering code 9 | 10 | (->> (z/of-string \"(+ 1 1) => (+ 2 2)\") 11 | (iterate z/right*) 12 | (take-while identity) 13 | (map z/node) 14 | (process-doc-nodes)) 15 | => \"(+ 1 1) => (+ 2 2)\" 16 | " 17 | {:added "0.1"} 18 | [docs] 19 | (->> docs 20 | (map (fn [node] 21 | (let [res (node/string node)] 22 | (cond (and (not (node/whitespace? node)) 23 | (not (node/comment? node)) 24 | (string? (node/value node))) 25 | (util/escape-newlines res) 26 | 27 | :else res)))) 28 | (string/join))) 29 | 30 | (defn link-references 31 | "link code for elements to references 32 | 33 | (link-references {:articles {\"example\" {:elements [{:type :reference :refer 'example.core/hello}]}} 34 | :references '{example.core {hello {:docs [] 35 | :source \"(defn hello [] 1)\"}}}} 36 | \"example\") 37 | => {:articles 38 | {\"example\" 39 | {:elements 40 | '[{:type :code, 41 | :refer example.core/hello, 42 | :origin :reference, 43 | :indentation 0, 44 | :code \"(defn hello [] 1)\", 45 | :mode :source, 46 | :title \"source of example.core/hello\"}]}}, 47 | :references '{example.core {hello {:docs [], :source \"(defn hello [] 1)\"}}}}" 48 | {:added "0.1"} 49 | [{:keys [references] :as folio} name] 50 | (update-in folio [:articles name :elements] 51 | (fn [elements] 52 | (mapv (fn [element] 53 | (if (-> element :type (= :reference)) 54 | (let [{:keys [refer mode]} element 55 | nsp (symbol (.getNamespace refer)) 56 | var (symbol (.getName refer)) 57 | mode (or mode :source) 58 | code (get-in references [nsp var mode]) 59 | code (case mode 60 | :source code 61 | :docs (process-doc-nodes code))] 62 | (-> element 63 | (assoc :type :code 64 | :origin :reference 65 | :indentation (case mode :source 0 :docs 2) 66 | :code code 67 | :mode mode) 68 | (update-in [:title] #(or % (str (clojure.core/name mode) " of " refer ""))))) 69 | element)) 70 | elements)))) 71 | -------------------------------------------------------------------------------- /src/hydrox/doc/link/stencil.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.stencil 2 | (:require [clojure.string :as string] 3 | [stencil.core :as stencil])) 4 | 5 | (def full-citation-pattern 6 | (string/join ["\\[\\[" 7 | "([^/^\\.^\\{^\\}^\\[^\\]]+)" 8 | "/" 9 | "([^/^\\.^\\{^\\}^\\[^\\]]+)" 10 | "\\]\\]"])) 11 | 12 | (def short-citation-pattern 13 | (string/join ["\\[\\[" 14 | "([^/^\\.^\\{^\\}^\\[^\\]]+)" 15 | "\\]\\]"])) 16 | 17 | (def full-pattern 18 | (string/join ["\\{\\{" 19 | "([^/^\\.^\\{^\\}]+)" 20 | "/" 21 | "([^/^\\.^\\{^\\}]+)" 22 | "\\}\\}"])) 23 | 24 | (def short-pattern 25 | (string/join ["\\{\\{" 26 | "([^/^\\.^\\{^\\}]+)" 27 | "\\}\\}"])) 28 | 29 | (defn transform-stencil 30 | "takes a short-form and expands using anchor information 31 | (transform-stencil \"{{hello}}\" \"example\" 32 | {\"example\" {\"hello\" {:number 1}}}) 33 | => \"1\" 34 | 35 | (transform-stencil \"{{stuff/create}}\" \"example\" 36 | {\"stuff\" {\"create\" {:number 2}}}) 37 | => \"2\" 38 | 39 | (transform-stencil \"[[stuff/create]]\" \"example\" 40 | {\"stuff\" {\"create\" {:number 2}}}) 41 | => \"[2](stuff.html#create)\" 42 | 43 | (transform-stencil \"[[create]]\" \"example\" 44 | {\"example\" {\"create\" {:number 2}}}) 45 | => \"[2](#create)\"" 46 | {:added "0.1"} 47 | [string name tags] 48 | (-> string 49 | (.replaceAll full-citation-pattern "[{{$1/$2}}]($1.html#$2)") 50 | (.replaceAll short-citation-pattern "[{{$1}}](#$1)") 51 | (.replaceAll full-pattern "{{$1.$2.number}}") 52 | (.replaceAll short-pattern (str "{{" name ".$1.number}}")) 53 | (stencil/render-string tags))) 54 | 55 | (defn link-stencil 56 | "links extra information for using the stencil format 57 | (link-stencil 58 | {:articles {\"example\" {:meta {:name \"world\"} 59 | :elements [{:type :paragraph 60 | :text \"{{PROJECT.version}} {{DOCUMENT.name}}\"} 61 | {:type :paragraph 62 | :text \"{{hello}} {{example.hello.label}}\"}]}} 63 | :project {:version \"0.1\"} 64 | :anchors {\"example\" {\"hello\" {:number 2 65 | :label \"two\"}}}} 66 | \"example\") 67 | => {:articles {\"example\" {:meta {:name \"world\"}, 68 | :elements [{:type :paragraph, :text \"0.1 world\"} 69 | {:type :paragraph, :text \"2 two\"}]}}, 70 | :project {:version \"0.1\"}, 71 | :anchors {\"example\" {\"hello\" {:number 2, :label \"two\"}}}}" 72 | {:added "0.1"} 73 | [folio name] 74 | (let [anchors (assoc (:anchors folio) 75 | :PROJECT (:project folio) 76 | :DOCUMENT (get-in folio [:articles name :meta]))] 77 | (update-in folio [:articles name :elements] 78 | (fn [elements] 79 | (->> elements 80 | (map (fn [element] 81 | (if (= :paragraph (:type element)) 82 | (update-in element [:text] 83 | transform-stencil name anchors) 84 | element)))))))) 85 | -------------------------------------------------------------------------------- /src/hydrox/doc/link/tags.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.tags 2 | (:require [hara.string.case :as case])) 3 | 4 | (def tag-required? 5 | #{:chapter :section :subsection :subsubsection :appendix :citation}) 6 | 7 | (def tag-optional? 8 | #{:ns :reference :image :equation}) 9 | 10 | (defn inc-candidate 11 | "creates an incremental version of a name 12 | 13 | (inc-candidate \"hello\") => \"hello-0\" 14 | (inc-candidate \"hello-1\") => \"hello-2\"" 15 | {:added "0.1"} 16 | [candidate] 17 | (if-let [[_ counter] (re-find #"-(\d+)$" candidate)] 18 | (let [len (count counter) 19 | num (Long/parseLong counter)] 20 | (str (subs candidate 0 (- (count candidate) len)) (inc num))) 21 | (str candidate "-0"))) 22 | 23 | (defn tag-string 24 | "creates a string that can be used as an anchor 25 | 26 | (tag-string \"hello.world/again\") 27 | => \"hello-world--again\"" 28 | {:added "0.1"} 29 | [s] 30 | (-> (case/spear-case s) 31 | (.replaceAll "\\." "-") 32 | (.replaceAll "/" "--") 33 | (.replaceAll "[^\\d^\\w^-]" ""))) 34 | 35 | (defn create-candidate 36 | "creates a candidate tag from a variety of sources 37 | 38 | (create-candidate {:origin :ns :ns 'clojure.core}) 39 | => \"ns-clojure-core\" 40 | 41 | (create-candidate {:title \"hello again\"}) 42 | => \"hello-again\" 43 | 44 | (create-candidate {:type :image :src \"http://github.com/hello/gather.jpeg\"}) 45 | => \"img-http----github-com--hello--gather-jpeg\"" 46 | {:added "0.1"} 47 | [{:keys [origin title type] :as element}] 48 | (cond origin 49 | (case origin 50 | :ns (tag-string (str "ns-" (:ns element))) 51 | :reference (tag-string (str (name (:mode element)) "-" (:refer element)))) 52 | 53 | title 54 | (tag-string title) 55 | 56 | (= :image type) 57 | (tag-string (str "img-" (:src element))))) 58 | 59 | (defn create-tag 60 | "creates a tag from an element 61 | 62 | (let [tags (atom #{}) 63 | result (create-tag {:title \"hello\"} tags)] 64 | [@tags result]) 65 | => [#{\"hello\"} {:title \"hello\", :tag \"hello\"}] 66 | 67 | (let [tags (atom #{\"hello\"}) 68 | result (create-tag {:title \"hello\"} tags)] 69 | [@tags result]) 70 | => [#{\"hello\" \"hello-0\"} {:title \"hello\", :tag \"hello-0\"}]" 71 | {:added "0.1"} 72 | ([element tags] 73 | (create-tag element tags (create-candidate element))) 74 | ([element tags candidate] 75 | (cond (nil? candidate) 76 | element 77 | 78 | (get @tags candidate) 79 | (create-tag element tags (inc-candidate candidate)) 80 | 81 | :else 82 | (do (swap! tags conj candidate) 83 | (assoc element :tag candidate))))) 84 | 85 | (defn link-tags 86 | "creates a tag for elements within the article 87 | (-> {:articles {\"example\" {:elements [{:type :chapter :title \"hello world\"} 88 | {:type :chapter :title \"hello world\"}]}}} 89 | (collect/collect-tags \"example\") 90 | (link-tags \"example\")) 91 | => {:articles 92 | {\"example\" 93 | {:elements [{:type :chapter, :title \"hello world\", :tag \"hello-world\"} 94 | {:type :chapter, :title \"hello world\", :tag \"hello-world-0\"}], 95 | :tags #{}}}}" 96 | {:added "0.1"} 97 | [{:keys [articles] :as folio} name] 98 | (let [tags (atom (get-in articles [name :tags]))] 99 | (let [auto-tag (->> (list (get-in articles [name :link :auto-tag]) 100 | (get-in folio [:meta :link :auto-tag]) 101 | true) 102 | (drop-while nil?) 103 | (first)) 104 | auto-tag (cond (set? auto-tag) auto-tag 105 | (false? auto-tag) #{} 106 | (true? auto-tag) tag-optional?)] 107 | (->> (get-in articles [name :elements]) 108 | (mapv (fn [element] 109 | (cond (and (or (tag-required? (:type element)) 110 | (auto-tag (:type element)) 111 | (auto-tag (:origin element))) 112 | (nil? (:tag element)) 113 | (not (:hidden element))) 114 | (create-tag element tags) 115 | 116 | :else element))) 117 | (assoc-in folio [:articles name :elements]))))) 118 | -------------------------------------------------------------------------------- /src/hydrox/doc/render.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render 2 | (:require [hydrox.common.util :as util] 3 | [hydrox.doc.render 4 | [article :as article] 5 | [navigation :as navigation] 6 | [toc :as toc]] 7 | [hiccup.compiler :as compiler] 8 | [clojure.string :as string])) 9 | 10 | (defn render-article [{:keys [elements]} folio] 11 | (->> elements 12 | (mapv #(article/render % folio)) 13 | (mapcat (fn [ele] (#'compiler/compile-seq [ele]))) 14 | (string/join))) 15 | 16 | (defn render-navigation [{:keys [elements]} folio] 17 | (let [chapters (filter (fn [e] (#{:chapter :appendix} (:type e))) 18 | elements)] 19 | (->> chapters 20 | (map #(navigation/render % folio)) 21 | (#'compiler/compile-seq) 22 | (string/join)))) 23 | 24 | (defn replace-template [template includes opts project] 25 | (reduce-kv (fn [^String html k v] 26 | (let [value (cond (string? v) 27 | v 28 | 29 | (vector? v) 30 | (cond (= (first v) :file) 31 | (slurp (util/full-path (second v) (-> opts :template :path) project))))] 32 | (.replaceAll html 33 | (str "<@=" (name k) ">") 34 | (util/escape-dollars value)))) 35 | template 36 | includes)) 37 | -------------------------------------------------------------------------------- /src/hydrox/doc/render/article.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render.article 2 | (:require [hydrox.doc.render.util :as util] 3 | [hydrox.doc.structure :as structure] 4 | [hydrox.doc.link.references :as references] 5 | [rewrite-clj.node :as node] 6 | [clojure.string :as string])) 7 | 8 | (defmulti render (fn [element folio] (:type element))) 9 | 10 | (defmethod render 11 | :paragraph 12 | [{:keys [text indentation] :as element} folio] 13 | [:div {:class :paragraph} 14 | (-> text 15 | (util/markup) 16 | (util/basic-html-unescape) 17 | (util/adjust-indent (or indentation 0)))]) 18 | 19 | (defmethod render 20 | :generic 21 | [{:keys [tag text elements]} folio] 22 | (vec (concat [:section] 23 | (map #(render % folio) elements)))) 24 | 25 | (defmethod render 26 | :chapter 27 | [{:keys [tag text number title elements] :as element} folio] 28 | (vec (concat [:section {:id tag :class :chapter} 29 | [:h2 {:class :chapter} (str number "    " title)]] 30 | (->> (structure/seperate #(= (:type %) :section) elements) 31 | (map (fn [group] 32 | (if (= :section (:type (first group))) 33 | (render (first group) folio) 34 | (vec (concat [:div {:class :group}] 35 | (map #(render % folio) group)))))))))) 36 | 37 | (defmethod render 38 | :appendix 39 | [{:keys [tag text number title elements]} folio] 40 | (vec (concat [:section {:id tag :class :chapter} 41 | [:h2 {:class :chapter} (str number "    " title)]] 42 | (map #(render % folio) elements)))) 43 | 44 | (defmethod render 45 | :section 46 | [{:keys [tag text number title elements]} folio] 47 | (vec (concat [:section {:id tag :class :section} 48 | [:h3 {:class :section} (str number "    " title)]] 49 | (map #(render % folio) elements)))) 50 | 51 | (defmethod render 52 | :subsection 53 | [{:keys [tag text number title elements]} folio] 54 | (vec (concat [:section {:id tag :class :subsection} 55 | [:h4 {:class :subsection} (str number "    " title)]] 56 | (map #(render % folio) elements)))) 57 | 58 | (defmethod render 59 | :subsubsection 60 | [{:keys [tag text number title elements]} folio] 61 | (vec (concat [:section {:id tag :class :subsubsection} 62 | [:h4 {:class :subsubsection} (str number "    " title)]] 63 | (map #(render % folio) elements)))) 64 | 65 | (defmethod render 66 | :code 67 | [{:keys [tag text code indentation lang number title] :as element} folio] 68 | [:div {:class :code} 69 | (if tag [:a {:name tag}]) 70 | (if number 71 | [:h5 (str "e." number 72 | (if title (str "  -  " title)))]) 73 | [:div {:hljs :hljs :no-escape :no-escape :language (or lang :clojure)} 74 | (-> code 75 | (util/join) 76 | (util/basic-html-escape) 77 | (util/adjust-indent indentation) 78 | (string/trimr) 79 | (string/trim-newline))]]) 80 | 81 | (defmethod render 82 | :block 83 | [element folio] 84 | (render (assoc element :type :code) folio)) 85 | 86 | (defmethod render 87 | :image 88 | [{:keys [tag title text number] :as element} folio] 89 | [:div {:class :figure} 90 | (if tag [:a {:name tag}]) 91 | [:div {:class "img"} 92 | [:img (dissoc element :number :type :tag :text :title)]] 93 | (if number 94 | [:h4 [:i (str "fig." number 95 | (if title (str "  -  " title)))]])]) 96 | 97 | (defmethod render 98 | :namespace 99 | [{:keys [mode] :as element} folio]) 100 | 101 | (defn render-api-index [namespace tag nsp] 102 | (->> nsp 103 | (map first) 104 | (map (fn [sym] 105 | [:a {:href (str "#" tag "--" sym)} (str sym)])) 106 | (#(interleave % (repeat "  "))) 107 | (apply vector :div [:a {:name tag}] 108 | [:h4 [:i "API"]]))) 109 | 110 | (defn render-api-elements [namespace tag nsp] 111 | (->> nsp 112 | (mapv (fn [[func data]] 113 | [:div 114 | [:a {:name (str tag "--" (name func))}] 115 | [:h4 (name func) 116 | " " [:a {:href (str "#" tag)} "▴"]] 117 | [:div {:hljs :hljs :no-escape :no-escape :language :clojure} 118 | (with-redefs [hydrox.meta.util/escape-newlines identity] 119 | (-> (:docs data) 120 | (references/process-doc-nodes) 121 | (util/join) 122 | (util/basic-html-escape) 123 | (util/adjust-indent 2) 124 | (string/trimr) 125 | (string/trim-newline)))]])) 126 | (apply vector :div))) 127 | 128 | (defmethod render 129 | :api 130 | [{:keys [namespace tag] :as element} folio] 131 | (let [tag (or tag (str "api-" (.replaceAll ^String namespace "\\." "-"))) 132 | nsp (-> folio 133 | :references 134 | (get (symbol namespace)) 135 | (->> (filter (fn [[_ data]] (:docs data))) 136 | (sort-by first)))] 137 | [:div {:class :api} 138 | [:hr] 139 | (render-api-index namespace tag nsp) 140 | [:hr] 141 | (render-api-elements namespace tag nsp)])) 142 | 143 | (comment 144 | 145 | [:p (-> folio 146 | :references 147 | (get (symbol namespace)) 148 | keys 149 | (map str))] 150 | 151 | #_(-> folio 152 | :references 153 | (get (symbol namespace)) 154 | first 155 | second 156 | :docs 157 | (references/process-doc-nodes))) 158 | -------------------------------------------------------------------------------- /src/hydrox/doc/render/navigation.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render.navigation 2 | (:require [hydrox.doc.render.util :as util])) 3 | 4 | (defmulti render (fn [element folio] (:type element))) 5 | 6 | (defmethod render 7 | :chapter 8 | [{:keys [tag number title elements] :as element} folio] 9 | [:li [:a {:href (str "#" tag)} (str number "    " title)] 10 | (let [sections (filter (fn [e] (= :section (:type e))) elements)] 11 | (if-not (empty? sections) 12 | (vec (concat [:ul {:class :nav}] 13 | (map #(render % folio) sections)))))]) 14 | 15 | (defmethod render 16 | :appendix 17 | [{:keys [tag number title elements]} folio] 18 | [:li [:a {:href (str "#" tag)} (str number "    " title)] 19 | (let [sections (filter (fn [e] (= :section (:type e))) elements)] 20 | (if-not (empty? sections) 21 | (vec (concat [:ul {:class :nav}] 22 | (map #(render % folio) sections)))))]) 23 | 24 | (defmethod render 25 | :section 26 | [{:keys [tag number title]} folio] 27 | [:li [:a {:href (str "#" tag)} (str number "    " title)]]) 28 | -------------------------------------------------------------------------------- /src/hydrox/doc/render/toc.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render.toc) 2 | -------------------------------------------------------------------------------- /src/hydrox/doc/render/util.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render.util 2 | (:require [clojure.string :as string] 3 | [markdown.core :as markdown] 4 | [hiccup.core :as html])) 5 | 6 | (defn adjust-indent 7 | "fixes indentation for code that is off slightly due to alignment 8 | 9 | (adjust-indent \"(+ 1\\n 2)\" 2) 10 | => \"(+ 1\\n 2)\"" 11 | {:added "0.1"} 12 | [s spaces] 13 | (->> (string/split-lines s) 14 | (map (fn [line] 15 | (if (and (< spaces (count line)) 16 | (re-find #"^\s+$" (subs line 0 spaces))) 17 | (subs line spaces) 18 | line))) 19 | (string/join "\n"))) 20 | 21 | (defn basic-html-escape 22 | "escapes characters using standard html format 23 | 24 | (basic-html-escape \"<>\") 25 | => \"<>\"" 26 | {:added "0.1"} 27 | [data] 28 | (clojure.string/escape data { \< "<" \> ">" \& "&" \" """ \\ "\"})) 29 | 30 | (defn basic-html-unescape 31 | "unescapes characters with standard html format 32 | 33 | (basic-html-unescape \"&quot;\") 34 | => \""\"" 35 | {:added "0.1"} 36 | [data] 37 | (let [out (-> data 38 | (.replaceAll "&quot;" """) 39 | (.replaceAll "&lt;" "<") 40 | (.replaceAll "&gt;" ">") 41 | (.replaceAll "&amp;" "&"))] 42 | out)) 43 | 44 | (defn markup 45 | "calls the markdown library to create markup from a string" 46 | {:added "0.1"} 47 | [data] 48 | (markdown/md-to-html-string data)) 49 | 50 | (defn join 51 | "like string/join but will return the input if it is a string 52 | 53 | (join \"hello\") => \"hello\" 54 | 55 | (join [\"hello\" \" \" \"world\"]) => \"hello world\"" 56 | {:added "0.1"} 57 | [data] 58 | (cond (string? data) 59 | data 60 | 61 | (vector? data) 62 | (string/join data))) 63 | -------------------------------------------------------------------------------- /src/hydrox/doc/structure.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.structure) 2 | 3 | (def containers 4 | #{:article :chapter :appendix :section :subsection :subsubsection :generic}) 5 | 6 | (def hierarchy 7 | (-> (make-hierarchy) 8 | (derive :article :generic) 9 | (derive :article :chapter) 10 | (derive :article :appendix) 11 | (derive :container :section) 12 | (derive :appendix :section) 13 | (derive :chapter :section) 14 | (derive :section :subsection) 15 | (derive :subsection :subsubsection))) 16 | 17 | (defn inclusive 18 | "determines which sections are contained by the other 19 | (inclusive :article :section) => true 20 | 21 | (inclusive :chapter :subsection) => true 22 | 23 | (inclusive :chapter :chapter) => false 24 | 25 | (inclusive :section :chapter) => false" 26 | {:added "0.1"} 27 | [x y] 28 | (if (get containers y) 29 | (if (not (isa? hierarchy y x)) 30 | true 31 | false) 32 | true)) 33 | 34 | (defn seperate 35 | "groups elements in an array 36 | (seperate #(= 1 %) [1 2 2 1 3 4 5]) 37 | => [[1 2 2] [1 3 4 5]]" 38 | {:added "0.1"} 39 | ([f v] 40 | (seperate f (rest v) [] (if (first v) 41 | [(first v)] 42 | []))) 43 | ([f [ele & more] all current] 44 | (cond (nil? ele) 45 | (conj all current) 46 | 47 | (f ele) 48 | (recur f more (conj all current) [ele]) 49 | 50 | :else 51 | (recur f more all (conj current ele))))) 52 | 53 | (defn containify 54 | "makes a nested vector object from a sequence of elements 55 | (containify [{:type :generic} 56 | {:type :paragraph} 57 | {:type :chapter} 58 | {:type :paragraph} 59 | {:type :section} 60 | {:type :paragraph} 61 | {:type :subsection} 62 | {:type :paragraph} 63 | {:type :section} 64 | {:type :chapter} 65 | {:type :section} 66 | {:type :appendix}]) 67 | => [{:type :article} 68 | [{:type :generic} 69 | [{:type :paragraph}]] 70 | [{:type :chapter} 71 | [{:type :paragraph}] 72 | [{:type :section} 73 | [{:type :paragraph}] 74 | [{:type :subsection} 75 | [{:type :paragraph}]]] 76 | [{:type :section}]] 77 | [{:type :chapter} 78 | [{:type :section}]] 79 | [{:type :appendix}]]" 80 | {:added "0.1"} 81 | ([v] 82 | (->> (containify v [#{:appendix :chapter :generic} :section :subsection :subsubsection]) 83 | (cons {:type :article}) 84 | vec)) 85 | ([v [tag & more]] 86 | (let [eq? (fn [tag ele] 87 | (if (set? tag) 88 | (get tag (:type ele)) 89 | (= tag (:type ele)))) 90 | subv (seperate (fn [ele] (eq? tag ele)) v)] 91 | (cond (empty? more) 92 | subv 93 | 94 | :else 95 | (mapv (fn [[head & body]] 96 | (cond 97 | (empty? body) [head] 98 | 99 | :else 100 | (vec (cons head (containify body more))))) 101 | subv))))) 102 | 103 | (declare mapify) 104 | 105 | (defn mapify-unit [x] 106 | (cond (map? x) x 107 | :else (mapify x))) 108 | 109 | (defn mapify [[head & more]] 110 | (cond (get containers (:type head)) 111 | (assoc head :elements (vec (flatten (map mapify-unit more)))) 112 | 113 | :else 114 | (mapv mapify-unit (cons head more)))) 115 | 116 | 117 | (defn structure 118 | "creates a nested map structure of elements and their containers 119 | (structure [{:type :generic} 120 | {:type :paragraph} 121 | {:type :chapter} 122 | {:type :paragraph} 123 | {:type :section} 124 | {:type :paragraph} 125 | {:type :subsection} 126 | {:type :paragraph} 127 | {:type :section} 128 | {:type :chapter} 129 | {:type :section} 130 | {:type :appendix}]) 131 | => {:type :article, 132 | :elements [{:type :generic, 133 | :elements [{:type :paragraph}]} 134 | {:type :chapter, 135 | :elements [{:type :paragraph} 136 | {:type :section, 137 | :elements [{:type :paragraph} 138 | {:type :subsection, 139 | :elements [{:type :paragraph}]}]} 140 | {:type :section, 141 | :elements []}]} 142 | {:type :chapter, 143 | :elements [{:type :section, :elements []}]} 144 | {:type :appendix, :elements []}]}" 145 | {:added "0.1"} 146 | [v] 147 | (-> v 148 | (containify) 149 | (mapify))) 150 | -------------------------------------------------------------------------------- /src/hydrox/meta.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.meta 2 | (:require [hydrox.meta.util :as util] 3 | [jai.query :as query] 4 | [rewrite-clj.zip :as source] 5 | [rewrite-clj.node :as node])) 6 | 7 | (defn selector 8 | "builds a selector for functions 9 | 10 | (selector 'hello) 11 | => '[(#{defn defmacro defmulti} | hello ^:%?- string? ^:%?- map? & _)]" 12 | {:added "0.1"} 13 | ([] (selector nil)) 14 | ([var] 15 | [(list '#{defn defmacro defmulti} '| (or var '_) '^:%?- string? '^:%?- map? '& '_)])) 16 | 17 | (defn edit-file 18 | "helper function for file manipulation used by import and purge" 19 | {:added "0.1"} 20 | [file var references edit-fn] 21 | (let [zloc (source/of-file file) 22 | nsp (-> (query/$ zloc [(ns | _ & _)] {:walk :top}) 23 | first) 24 | edit (edit-fn nsp references) 25 | zloc (-> zloc 26 | (query/modify (selector var) 27 | edit 28 | {:walk :top}))] 29 | (util/write-to-file zloc file))) 30 | 31 | (defn import-fn 32 | "helper function for file import" 33 | {:added "0.1"} 34 | [nsp references] 35 | (fn [zloc] 36 | (util/import-location zloc nsp references))) 37 | 38 | (defn import-var 39 | "import docs for a single var" 40 | {:added "0.1"} 41 | [file var references] 42 | (edit-file file var references import-fn)) 43 | 44 | (defn import-file 45 | "import docs for a file" 46 | {:added "0.1"} 47 | [file references] 48 | (edit-file file nil references import-fn)) 49 | 50 | (defn import-project 51 | "import docs for the entire project" 52 | {:added "0.1"} 53 | [project references] 54 | (for [file (util/all-files project :source-paths ".clj")] 55 | (import-file file references))) 56 | 57 | (defn purge-fn 58 | "helper function for file purge" 59 | {:added "0.1"} 60 | [nsp references] 61 | identity) 62 | 63 | (defn purge-var 64 | "purge docs for a single var" 65 | {:added "0.1"} 66 | [file var] 67 | (edit-file file var nil purge-fn)) 68 | 69 | (defn purge-file 70 | "purge docs for a file" 71 | {:added "0.1"} 72 | [file] 73 | (edit-file file nil nil purge-fn)) 74 | 75 | (defn purge-project 76 | "purge docs for the entire project" 77 | {:added "0.1"} 78 | [project] 79 | (for [file (util/all-files project :source-paths ".clj")] 80 | (purge-file file))) 81 | -------------------------------------------------------------------------------- /src/hydrox/meta/util.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.meta.util 2 | (:require [clojure.zip :as zip] 3 | [clojure.string :as string] 4 | [rewrite-clj.zip :as source] 5 | [rewrite-clj.node :as node] 6 | [clojure.java.io :as io])) 7 | 8 | (defn append-node 9 | "Adds node as well as whitespace and newline on right 10 | 11 | (-> (z/of-string \"(+)\") 12 | (z/down) 13 | (append-node 2) 14 | (append-node 1) 15 | (z/->root-string)) 16 | => \"(+\\n 1\\n 2)\"" 17 | {:added "0.1"} 18 | [zloc node] 19 | (if node 20 | (-> zloc 21 | (zip/insert-right node) 22 | (zip/insert-right (node/whitespace-node " ")) 23 | (zip/insert-right (node/newline-node "\n"))) 24 | zloc)) 25 | 26 | (defn has-quotes? 27 | "checks if a string has quotes 28 | 29 | (has-quotes? \"\\\"hello\\\"\") 30 | => true" 31 | {:added "0.1"} 32 | [s] 33 | (and (.startsWith s "\"") 34 | (.endsWith s "\""))) 35 | 36 | (defn strip-quotes 37 | "gets rid of quotes in a string 38 | 39 | (strip-quotes \"\\\"hello\\\"\") 40 | => \"hello\"" 41 | {:added "0.1"} 42 | [s] 43 | (if (has-quotes? s) 44 | (subs s 1 (dec (count s))) 45 | s)) 46 | 47 | (defn escape-newlines 48 | "makes sure that newlines are printable 49 | 50 | (escape-newlines \"\\\n\") 51 | => \"\\n\"" 52 | {:added "0.1"} 53 | [s] 54 | (-> s 55 | (.replaceAll "\\n" "\\\\n"))) 56 | 57 | (defn escape-escapes 58 | "makes sure that newlines are printable 59 | 60 | (escape-escapes \"\\\n\") 61 | => \"\\\n\"" 62 | {:added "0.1"} 63 | [s] 64 | (-> s 65 | (.replaceAll "(\\\\)([A-Za-z])" "$1$1$2"))) 66 | 67 | (defn escape-quotes 68 | "makes sure that quotes are printable in string form 69 | 70 | (escape-quotes \"\\\"hello\\\"\") 71 | => \"\\\"hello\\\"\"" 72 | {:added "0.1"} 73 | [s] 74 | (-> s 75 | (.replaceAll "(\\\\)?\"" "$1$1\\\\\\\""))) 76 | 77 | (defn strip-quotes-array 78 | "utility that strips quotes when not the result of a fact 79 | (strip-quotes-array [\"\\\"hello\\\"\"]) 80 | => [\"hello\"] 81 | 82 | (strip-quotes-array [\"(str \\\"hello\\\")\" \" \" \"=>\" \" \" \"\\\"hello\\\"\"]) 83 | => [\"(str \\\"hello\\\")\" \" \" \"=>\" \" \" \"\\\"hello\\\"\"]" 84 | {:added "0.1"} 85 | ([arr] (strip-quotes-array arr nil nil [])) 86 | ([[x & more] p1 p2 out] 87 | (cond (nil? x) 88 | out 89 | 90 | :else 91 | (recur more x p1 (conj out (if (= p2 "=>") 92 | (if (has-quotes? x) 93 | (escape-newlines x) 94 | x) 95 | (strip-quotes x))))))) 96 | 97 | (defn nodes->docstring 98 | "converts nodes to a docstring compatible 99 | (->> (z/of-string \"\\\"hello\\\"\\n (+ 1 2)\\n => 3 \") 100 | (iterate z/right*) 101 | (take-while identity) 102 | (map z/node) 103 | (nodes->docstring) 104 | (node/string)) 105 | => \"\"hello\\n (+ 1 2)\\n => 3 \"\" 106 | 107 | (->> (z/of-string (str [\\e \\d])) 108 | (iterate z/right*) 109 | (take-while identity) 110 | (map z/node) 111 | (nodes->docstring) 112 | (str) 113 | (read-string)) 114 | => \"[\\e \\d]\"" 115 | {:added "0.1"} 116 | [nodes] 117 | (->> nodes 118 | (map node/string) 119 | (strip-quotes-array) 120 | (string/join) 121 | (escape-escapes) 122 | (escape-quotes) 123 | (string/split-lines) 124 | (map-indexed (fn [i s] 125 | (str (if-not (or (zero? i) 126 | (= i (dec (count nodes)))) 127 | " ") 128 | s))) 129 | (node/string-node))) 130 | 131 | (defn import-location 132 | "imports the meta information and docstring" 133 | {:added "0.1"} 134 | [zloc nsp gathered] 135 | (let [sym (source/sexpr zloc) 136 | nodes (get-in gathered [nsp sym :docs]) 137 | meta (get-in gathered [nsp sym :meta])] 138 | (-> zloc 139 | (append-node meta) 140 | (append-node (if nodes (nodes->docstring nodes)))))) 141 | 142 | (defn write-to-file 143 | "exports the zipper contents to file" 144 | {:added "0.1"} 145 | [zloc file] 146 | (->> (iterate source/right* zloc) 147 | (take-while identity) 148 | (map source/node) 149 | (map node/string) 150 | (string/join) 151 | (spit file))) 152 | 153 | (defn all-files 154 | "finds all files in the project given a context 155 | 156 | (->> (all-files {:root (.getCanonicalPath (io/file \"example\"))} :root \"md\") 157 | (map #(.getName %))) 158 | => [\"README.md\"]" 159 | {:added "0.1"} 160 | [project path-type extension] 161 | (->> project 162 | path-type 163 | (#(if (sequential? %) % [%])) 164 | (mapcat (fn [dir] (file-seq (io/file (if (.startsWith dir "/") 165 | dir 166 | (str (:root project) "/" dir)))))) 167 | (filter (fn [file] (and (.isFile file) 168 | (.endsWith (str file) extension)))) 169 | (map #(.getCanonicalFile %)))) 170 | -------------------------------------------------------------------------------- /template/article.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <@=title> 11 | 12 | <@=dependencies> 13 | 14 | 19 | 20 | 23 | 24 | 25 | 26 | <@=navbar> 27 | 28 |
29 |
30 | 33 |
34 |
35 |
36 |
37 |

<@=title>

38 |

<@=subtitle>

39 |
40 |
41 | <@=article> 42 |
43 |
44 | 45 | 46 | 47 | 54 | 55 | 56 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /template/assets/css/scrollspy.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background: #f8f8f8; 4 | position: relative; 5 | line-height: 1.5; 6 | font-family: 'Raleway', sans-serif; 7 | } 8 | 9 | h1, h2 { 10 | font-weight: 700; 11 | } 12 | 13 | h2.chapter{ 14 | font-weight: 900; 15 | margin-top: 50px; 16 | } 17 | 18 | h3 span { 19 | color: #286090; 20 | } 21 | 22 | .jumbotron { 23 | border-radius: 0px; 24 | background: #ffffff; 25 | margin-bottom: 0; 26 | text-align: center; 27 | border: 1px solid transparent; 28 | background: #ffffff; 29 | border: 1px solid; 30 | border-radius: 2px; 31 | border-color: #e9e9e9; 32 | } 33 | 34 | .jumbotron button { 35 | margin-top: 10px; 36 | } 37 | 38 | .chapter > div{ 39 | margin-top: 30px; 40 | margin-bottom: 10px; 41 | padding: 20px; 42 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 43 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 44 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 45 | background: #ffffff; 46 | border: 1px solid; 47 | border-radius: 2px; 48 | border-color: #e9e9e9; 49 | 50 | } 51 | 52 | section.section { 53 | margin-top: 30px; 54 | margin-bottom: 10px; 55 | padding: 20px; 56 | /*-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 57 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 58 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);*/ 59 | background: #ffffff; 60 | border: 1px solid transparent; 61 | border-radius: 2px; 62 | border-color: #e9e9e9; 63 | } 64 | 65 | section.subsection { 66 | margin-top: 30px; 67 | } 68 | 69 | section:last-child { 70 | border-bottom: none; 71 | } 72 | 73 | #content{ 74 | margin-top: 30px; 75 | } 76 | 77 | #nav{ 78 | margin-top: 50px; 79 | } 80 | 81 | 82 | .nav { 83 | background: #30426a; 84 | } 85 | 86 | .nav .contents{ 87 | color: white; 88 | padding: 18px 0px 15px 30px; 89 | background: #2d3e63; 90 | } 91 | 92 | .nav a { 93 | color: #b2bfdc; 94 | font-style: italic; 95 | } 96 | 97 | .nav li a:hover, 98 | .nav li a:focus { 99 | background: #2d3e63; 100 | } 101 | 102 | .nav .active { 103 | font-weight: bold; 104 | /*background: #72bcd4;*/ 105 | } 106 | 107 | .nav .nav { 108 | display: none; 109 | } 110 | 111 | .nav .active .nav { 112 | display: block; 113 | } 114 | 115 | .nav>li>a { 116 | padding-top: 5px; 117 | padding-left: 30px; 118 | } 119 | .nav .nav a { 120 | font-weight: normal; 121 | text-decoration: none; 122 | font-size: .85em; 123 | padding-left: 40px; 124 | } 125 | 126 | .nav .nav span { 127 | margin: 0 5px 0 2px; 128 | } 129 | 130 | .nav .nav .active a, 131 | .nav .nav .active:hover a, 132 | .nav .nav .active:focus a { 133 | text-decoration: underline; 134 | /*padding-left: 30px; 135 | border-left: 5px solid black;*/ 136 | } 137 | 138 | .nav .nav .active span, 139 | .nav .nav .active:hover span, 140 | .nav .nav .active:focus span { 141 | display: none; 142 | } 143 | 144 | .application { 145 | border-top: 1px solid #c1e1ec; 146 | } 147 | 148 | .affix-top { 149 | position: relative; 150 | } 151 | 152 | .affix { 153 | top: 0px; 154 | } 155 | 156 | .affix, 157 | .affix-bottom { 158 | /*width: 213px;*/ 159 | } 160 | 161 | .affix-bottom { 162 | position: absolute; 163 | } 164 | 165 | footer { 166 | border-top: 1px solid #c1e1ec; 167 | height: 50px; 168 | } 169 | 170 | footer p { 171 | line-height: 50px; 172 | margin-bottom: 0; 173 | } 174 | 175 | @media (min-width:1200px) { 176 | .affix, 177 | .affix-bottom { 178 | width: 263px; 179 | } 180 | } 181 | 182 | .img img { 183 | display: block; 184 | margin-top: 10px; 185 | margin-right: auto; 186 | margin-left: auto; 187 | } 188 | 189 | .figure { 190 | border: solid 1px #ccc; 191 | padding: 10px; 192 | margin-bottom: 10px; 193 | } 194 | 195 | .figure h4{ 196 | text-align: center; 197 | margin-top:30px 198 | } 199 | 200 | .code h5{ 201 | font-weight: 900; 202 | margin-top: 20px; 203 | margin-bottom: 3px; 204 | } 205 | 206 | 207 | ul.sidebar .submenu-item { 208 | margin-left: 10px; 209 | } 210 | 211 | nav { 212 | background-color: #30426A; 213 | border-color: #30426A; 214 | } 215 | 216 | .navbar-brand img{ 217 | height: 45px; 218 | margin-top: -10px; 219 | } 220 | 221 | .navbar-nav { 222 | margin-top: 12px; 223 | } 224 | 225 | .nav .open>a, .nav .open>a:focus, .nav .open>a:hover { 226 | background-color: #30426A; 227 | } 228 | 229 | .dropdown-menu>li>a:hover { 230 | color: white; 231 | } 232 | -------------------------------------------------------------------------------- /template/assets/img/big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/template/assets/img/big.png -------------------------------------------------------------------------------- /template/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/template/assets/img/favicon.png -------------------------------------------------------------------------------- /template/assets/img/hydrox-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/template/assets/img/hydrox-overview.png -------------------------------------------------------------------------------- /template/assets/img/hydrox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/template/assets/img/hydrox.png -------------------------------------------------------------------------------- /template/assets/img/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/template/assets/img/logo-white.png -------------------------------------------------------------------------------- /template/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpshift/hydrox/2beb3c56fad43bbf16f07db7ee72c5862978350c/template/assets/img/logo.png -------------------------------------------------------------------------------- /template/assets/js/angular-highlightjs.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-highlightjs 2 | version: 0.4.1 3 | build date: 2015-02-03 4 | author: Chih-Hsuan Fan 5 | https://github.com/pc035860/angular-highlightjs.git */ 6 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="hljs"),function(a,b){function c(a){var c=!0;return b.forEach(["source","include"],function(b){a[b]&&(c=!1)}),c}var d=b.module("hljs",[]);d.provider("hljsService",function(){var a={};return{setOptions:function(c){b.extend(a,c)},getOptions:function(){return b.copy(a)},$get:["$window",function(c){return(c.hljs.configure||b.noop)(a),c.hljs}]}}),d.factory("hljsCache",["$cacheFactory",function(a){return a("hljsCache")}]),d.controller("HljsCtrl",["hljsCache","hljsService",function(a,c){var d=this,e=null,f=null,g=null,h=null;d.init=function(a){e=a},d.setLanguage=function(a){f=a,g&&d.highlight(g)},d.highlightCallback=function(a){h=a},d.highlight=function(i){if(e){var j,k;g=i,f?(k=d._cacheKey(f,g),j=a.get(k),j||(j=c.highlight(f,c.fixMarkup(g),!0),a.put(k,j))):(k=d._cacheKey(g),j=a.get(k),j||(j=c.highlightAuto(c.fixMarkup(g)),a.put(k,j))),e.html(j.value),e.addClass(j.language),null!==h&&b.isFunction(h)&&h()}},d.clear=function(){e&&(g=null,e.text(""))},d.release=function(){e=null},d._cacheKey=function(){var a=Array.prototype.slice.call(arguments),b="!angular-highlightjs!";return a.join(b)}}]);var e,f,g,h;e=["$compile","$parse",function(a,d){return{restrict:"EA",controller:"HljsCtrl",compile:function(e){var f=e[0].innerHTML.replace(/^(\r\n|\r|\n)/m,""),g=e[0].textContent.replace(/^(\r\n|\r|\n)/m,"");return e.html('
'),function(e,h,i,j){var k,l;if(b.isDefined(i.compile)&&(k=d(i.compile)),b.isDefined(i.escape)?l=d(i.escape):b.isDefined(i.noEscape)&&(l=d("false")),j.init(h.find("code")),i.onhighlight&&j.highlightCallback(function(){e.$eval(i.onhighlight)}),(f||g)&&c(i)){var m;m=l&&!l(e)?g:f,j.highlight(m),k&&k(e)&&a(h.find("code").contents())(e)}e.$on("$destroy",function(){j.release()})}}}}],f=function(a){return[function(){return{require:"?hljs",restrict:"A",link:function(c,d,e,f){f&&e.$observe(a,function(a){b.isDefined(a)&&f.setLanguage(a)})}}}]},g=function(a){return["$compile","$parse",function(c,d){return{require:"?hljs",restrict:"A",link:function(e,f,g,h){var i;h&&(b.isDefined(g.compile)&&(i=d(g.compile)),e.$watch(g[a],function(a){a?(h.highlight(a),i&&i(e)&&c(f.find("code").contents())(e)):h.clear()}))}}}]},h=function(a){return["$http","$templateCache","$q","$compile","$parse",function(c,d,e,f,g){return{require:"?hljs",restrict:"A",compile:function(h,i){var j=i[a];return function(a,h,i,k){var l,m=0;k&&(b.isDefined(i.compile)&&(l=g(i.compile)),a.$watch(j,function(g){var i=++m;if(g&&b.isString(g)){var j,n;j=d.get(g),j||(n=e.defer(),c.get(g,{cache:d,transformResponse:function(a){return a}}).success(function(a){i===m&&n.resolve(a)}).error(function(){i===m&&k.clear(),n.resolve()}),j=n.promise),e.when(j).then(function(c){c&&(b.isArray(c)?c=c[1]:b.isObject(c)&&(c=c.data),c=c.replace(/^(\r\n|\r|\n)/m,""),k.highlight(c),l&&l(a)&&f(h.find("code").contents())(a))})}else k.clear()}))}}}}]},d.directive("hljs",e).directive("language",f("language")).directive("source",g("source")).directive("include",h("include"))}(window,window.angular); -------------------------------------------------------------------------------- /template/partials/deps-web.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /template/partials/navbar.html: -------------------------------------------------------------------------------- 1 | 2 | 21 | -------------------------------------------------------------------------------- /test/documentation/hydrox_guide/bug_example.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.hydrox-guide.bug-example 2 | (:require [midje.sweet :refer :all])) 3 | 4 | [[:chapter {:title "A Bug's Life"}]] 5 | 6 | [[:section {:title "Version One"}]] 7 | 8 | "A new clojure project is created." 9 | 10 | [[{:lang "bash" :numbered false}]] 11 | (comment 12 | > lein new fami 13 | > cd fami 14 | > lein repl) 15 | 16 | " 17 | A very useful function `add-5` has been defined:" 18 | 19 | [[{:tag "add-5-fn" :title "src/fami/operations.clj"}]] 20 | (comment 21 | (ns fami.operations) 22 | 23 | (defn add-5 24 | "add-5 is a function that takes any number of arguments 25 | and adds 5 to the sum" 26 | [& ns] 27 | (apply + 5 ns))) 28 | 29 | "And the corresponding tests specified:" 30 | 31 | [[{:tag "add-5-tests" :title "src/fami/test-operations.clj"}]] 32 | (comment 33 | (ns fami.test-operations 34 | (:require [fami.operations :refer :all] 35 | [midje.sweet :refer :all])) 36 | 37 | (fact "add-5 should increment any list of numbers by 5" 38 | (add-5 5) => 10 39 | (add-5 1 2 3 4) => 15)) 40 | 41 | "There are additional entries for the operation in the readme as well as also being scattered around in various other documents." 42 | [[{:tag "add-5-readme" :title "readme.md, operations.md"}]] 43 | (comment 44 | ... 45 | 46 | Here are some of the use cases for add-5 47 | 48 | (add-5 5) ;; => 10 49 | (add-5 1 2 3 4) ;; => 15 50 | 51 | ...) 52 | 53 | "This version of this library has been released as **version 1.0**" 54 | 55 | [[:section {:title "Version Two"}]] 56 | 57 | "The library is super successful with many users. The code undergoes refactoring and it is decided that the original `add-5` is too powerful and so it must be muted to only accept one argument. An additional function `add-5-multi` is used to make explicit that the function is taking multiple arguments." 58 | 59 | [[{:tag "add-5-v2" :title "src/fami/operations.clj - v2"}]] 60 | (comment 61 | (ns fami.operations) 62 | 63 | (defn add-5 [n] ;; The muted version 64 | "add-5 is a function that takes a number and adds 5 to it" 65 | (+ n 5)) 66 | 67 | (defn add-5-multi 68 | "add-5-multi is a function that takes any number of arguments 69 | and adds 5 to the sum" 70 | [& ns] 71 | (apply + 5 ns))) 72 | 73 | "The tests throw an exception but are quickly fixed:" 74 | 75 | [[{:tag "add-5-v2-tests" :title "src/fami/test-operations.clj"}]] 76 | (comment 77 | (ns fami.test-operations 78 | (:require [fami.operations :refer :all] 79 | [midje.sweet :refer :all])) 80 | 81 | (fact "add-5 should increment only one input by 5" 82 | (add-5 5) => 10 83 | (add-5 1 2) => (throws clojure.lang.ArityException)) 84 | 85 | (fact "add-5-multi should increment any list of numbers by 5" 86 | (add-5-multi 1 2 3 4) => 15)) 87 | 88 | "This version of this library has been released as **version 2.0**" 89 | 90 | 91 | [[:section {:title "The Bug Surfaces"}]] 92 | 93 | "Although the tests are correct, the documentation is not. Anyone using this library can potentially have the `clojure.lang.ArityException` bug if they carefully followed instructions in the documentation. 94 | 95 | This is a trival example of a much greater problem. When a project begins to evolve and codebase begins to change, the documentation then becomes incorrect. Although source and test code can be isolated through testing, fixing documentation is a miserable and futile exercise of cut and paste. With no real tool to check whether code is still valid, the documentation become less and less correct until all the examples have to be rechecked and the documention rewritten. 96 | 97 | **Then the codebase changes again ...** 98 | 99 | Once the library has been release to the world and people have already started using it, there is no taking it back. Bugs propagate through miscommunication. Miscommunication with machines can usually be contained and fixed. Miscommunication with people becomes potentially more difficult to contain. 100 | 101 | This simple scenario highlights the problem that developers have when updating documentation. When we cannot `prove` that our documentation is correct, then the cost of fixing out of date documentation becomes bigger and bigger as time progresses. With `hydrox` this process just became much more cost effective." 102 | -------------------------------------------------------------------------------- /test/documentation/sample_document.clj: -------------------------------------------------------------------------------- 1 | (ns documentation.sample-document 2 | (:require [midje.sweet :refer :all] 3 | [hydrox.core :as hydrox])) 4 | 5 | [[:chapter {:tag "hello" :title "Introduction"}]] 6 | 7 | "This is an introduction to the exciting library" 8 | 9 | [[:section {:title "Defining a function"}]] 10 | 11 | "We define function `add-5` below" 12 | 13 | [[{:numbered false}]] 14 | (defn add-5 [x] 15 | (+ x 5)) 16 | 17 | [[:section {:title "Testing a function"}]] 18 | 19 | "`add-5` outputs the following results seen in 20 | [e.{{add-5-1}}](#add-5-1) and [e.{{add-5-10}}](#add-5-10):" 21 | 22 | [[{:tag "eq1"}]] 23 | (fact 24 | (+ 1 0) => 1 25 | (+ 1 3) => 4) 26 | 27 | ;;[[{:tag "eq2" :title "some equations 2"}]] 28 | (comment 29 | (+ 1 1) => 2 30 | (+ 1 3) => 2) 31 | 32 | (facts 33 | [[{:tag "add-5-1" :title "1 add 5 equals 6"}]] 34 | (add-5 1) => 6 35 | 36 | [[{:tag "add-5-10" :title "10 add 5 equals 15"}]] 37 | (add-5 10) => 15) 38 | 39 | [[:chapter {:title "Walkthrough"}]] 40 | 41 | "Here is a walkthrough for the library" 42 | 43 | [[:chapter {:title "API Reference"}]] 44 | 45 | "The API reference here:" 46 | -------------------------------------------------------------------------------- /test/hydrox/analyse/source_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.source-test 2 | (:use midje.sweet) 3 | (:require [hydrox.analyse.source :refer :all])) 4 | 5 | ^{:refer hydrox.analyse.source/analyse-source-file :added "0.1"} 6 | (fact "analyses a source file for namespace and function definitions" 7 | (analyse-source-file "example/src/example/core.clj" {}) 8 | => '{example.core 9 | {foo 10 | {:source "(defn foo\n [x]\n (println x \"Hello, World!\"))"}}}) 11 | -------------------------------------------------------------------------------- /test/hydrox/analyse/test/clojure_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test.clojure-test 2 | (:use midje.sweet) 3 | (:require [hydrox.analyse.test.clojure :refer :all] 4 | [hydrox.analyse.test.common :as common] 5 | [rewrite-clj.zip :as z])) 6 | 7 | ^{:refer hydrox.analyse.test.clojure/gather-is-form :added "0.1"} 8 | (fact "Make docstring notation out of is form" 9 | (common/join-nodes (gather-is-form (z/of-string "(is (= 1 1))"))) 10 | => "1\n => 1" 11 | 12 | (common/join-nodes (gather-is-form (z/of-string "(is (boolean? 4))"))) 13 | => "(boolean? 4)\n => true") 14 | 15 | ^{:refer hydrox.analyse.test.clojure/gather-deftest :added "0.1"} 16 | (fact "Make docstring notation out of deftest form" 17 | (-> "^{:refer example/hello-world :added \"0.1\"} 18 | (deftest hello-world-test\n \"Sample test program\"\n (is (= 1 1))\n (is (identical? 2 4)))" 19 | (z/of-string) 20 | z/down z/right z/down z/right z/right 21 | (gather-deftest) 22 | :docs 23 | common/join-nodes) 24 | => "\"Sample test program\"\n 1\n => 1\n (identical? 2 4)\n => true") 25 | -------------------------------------------------------------------------------- /test/hydrox/analyse/test/common_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test.common-test 2 | (:use midje.sweet) 3 | (:require [hydrox.analyse.test.common :refer :all] 4 | [rewrite-clj.zip :as z])) 5 | 6 | ^{:refer hydrox.analyse.test.common/gather-meta :added "0.1"} 7 | (fact "gets the metadata for a particular form" 8 | (-> (z/of-string "^{:refer clojure.core/+ :added \"0.1\"}\n(fact ...)") 9 | z/down z/right z/down 10 | gather-meta) 11 | => '{:added "0.1", :ns clojure.core, :var +, :refer clojure.core/+}) 12 | 13 | ^{:refer hydrox.analyse.test.common/gather-string :added "0.1"} 14 | (fact "creates correctly spaced code string from normal docstring" 15 | 16 | (-> (z/of-string "\"hello\nworld\nalready\"") 17 | (gather-string) 18 | (str)) 19 | => "\"hello\n world\n already\"") 20 | 21 | ^{:refer hydrox.analyse.test.common/strip-quotes :added "0.1"} 22 | (fact "takes away the quotes from a string for formatting purposes" 23 | 24 | (strip-quotes "\"hello\"") 25 | => "hello") 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/hydrox/analyse/test/midje_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test.midje-test 2 | (:use midje.sweet) 3 | (:require [hydrox.analyse.test.midje :refer :all] 4 | [hydrox.analyse.test.common :as common] 5 | [rewrite-clj.zip :as z])) 6 | 7 | ^{:refer hydrox.analyse.test.midje/gather-fact :added "0.1"} 8 | (fact "Make docstring notation out of fact form" 9 | (-> "^{:refer example/hello-world :added \"0.1\"} 10 | (fact \"Sample test program\"\n (+ 1 1) => 2\n (long? 3) => true)" 11 | (z/of-string) 12 | z/down z/right z/down z/right 13 | (gather-fact) 14 | :docs 15 | common/join-nodes) 16 | => "\"Sample test program\"\n (+ 1 1) => 2\n (long? 3) => true") 17 | -------------------------------------------------------------------------------- /test/hydrox/analyse/test_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse.test-test 2 | (:use midje.sweet) 3 | (:require [hydrox.analyse.test :refer :all] 4 | [hydrox.analyse.test.common :as common])) 5 | 6 | ^{:refer hydrox.analyse.test/find-frameworks :added "0.1"} 7 | (fact "find test frameworks given a namespace form" 8 | (find-frameworks '(ns ... 9 | (:use midje.sweet))) 10 | => #{:midje} 11 | 12 | (find-frameworks '(ns ... 13 | (:use clojure.test))) 14 | => #{:clojure}) 15 | 16 | ^{:refer hydrox.analyse.test/analyse-test-file :added "0.1"} 17 | (fact "analyses a test file for docstring forms" 18 | (-> (analyse-test-file "example/test/example/core_test.clj" {}) 19 | (update-in '[example.core foo :docs] common/join-nodes)) 20 | => '{example.core {foo {:docs "1\n => 1", :meta {:added "0.1"}}}}) 21 | -------------------------------------------------------------------------------- /test/hydrox/analyse_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.analyse-test 2 | (:use midje.sweet) 3 | (:require [hydrox.analyse :refer :all] 4 | [hydrox.core :as hydrox] 5 | [hydrox.common.util :as util] 6 | [clojure.java.io :as io])) 7 | 8 | (defmacro contains-in [m] 9 | (cond (map? m) 10 | `(contains ~(reduce-kv (fn [out k v] 11 | (assoc out k `(contains-in ~v))) 12 | {} 13 | m)) 14 | :else m)) 15 | 16 | (def user-dir (System/getProperty "user.dir")) 17 | 18 | ^{:refer hydrox.analyse/canonical :added "0.1"} 19 | (fact "returns the canonical system path" 20 | 21 | (canonical "src") 22 | => (str (System/getProperty "user.dir") "/src")) 23 | 24 | ^{:refer hydrox.analyse/file-type :added "0.1"} 25 | (fact "returns the file-type for entries" 26 | 27 | (file-type {:source-paths ["src"] 28 | :test-paths ["test"]} (io/file "src/code.clj")) 29 | => :source) 30 | 31 | ^{:refer hydrox.analyse/add-file :added "0.1"} 32 | (fact "adds a file to the folio" 33 | (-> {:project (util/read-project (io/file "example/project.clj"))} 34 | (add-file (io/file "example/test/example/core_test.clj")) 35 | (add-file (io/file "example/src/example/core.clj")) 36 | (dissoc :project)) 37 | => (contains-in 38 | {:registry {(str user-dir "/example/test/example/core_test.clj") 39 | {'example.core 40 | {'foo {:docs vector?, :meta {:added "0.1"}}}}, 41 | (str user-dir "/example/src/example/core.clj") 42 | {'example.core 43 | {'foo {:source "(defn foo\n [x]\n (println x \"Hello, World!\"))"}}}}, 44 | :references {'example.core 45 | {'foo {:docs vector?, :meta {:added "0.1"}, 46 | :source "(defn foo\n [x]\n (println x \"Hello, World!\"))"}}}, 47 | :namespace-lu {'example.core (str user-dir "/example/src/example/core.clj")}})) 48 | 49 | ^{:refer hydrox.analyse/remove-file :added "0.1"} 50 | (fact "removes a file to the folio" 51 | (-> {:project (util/read-project (io/file "example/project.clj"))} 52 | (add-file (io/file "example/src/example/core.clj")) 53 | (remove-file (io/file "example/src/example/core.clj")) 54 | (dissoc :project)) 55 | => {:registry {} 56 | :references {} 57 | :namespace-lu {}}) 58 | -------------------------------------------------------------------------------- /test/hydrox/common/data_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.common.data-test 2 | (:use midje.sweet) 3 | (:require [hydrox.common.data :refer :all])) 4 | 5 | ^{:refer hydrox.common.data/folio :added "0.1"} 6 | (fact "constructs a folio object") 7 | 8 | ^{:refer hydrox.common.data/references :added "0.1"} 9 | (fact "constructs a reference object") 10 | -------------------------------------------------------------------------------- /test/hydrox/common/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.common.util-test 2 | (:use midje.sweet) 3 | (:require [hydrox.common.util :refer :all] 4 | [clojure.java.io :as io])) 5 | 6 | ^{:refer hydrox.common.util/full-path :added "0.1"} 7 | (fact "constructs a path from a project" 8 | 9 | (full-path "example/file.clj" "src" {:root "/home/user"}) 10 | => "/home/user/src/example/file.clj") 11 | 12 | ^{:refer hydrox.common.util/filter-pred :added "0.1"} 13 | (fact "filters values of a map that fits the predicate" 14 | (filter-pred string? {:a "valid" :b 0}) 15 | => {:a "valid"}) 16 | 17 | ^{:refer hydrox.common.util/escape-dollars :added "0.1"} 18 | (fact "for regex purposes, escape dollar signs in templates") 19 | 20 | ^{:refer hydrox.common.util/read-project :added "0.1"} 21 | (fact "like `leiningen.core.project/read` but with less features'" 22 | 23 | (keys (read-project (io/file "example/project.clj"))) 24 | => (just [:description :license :name :source-paths :test-paths 25 | :documentation :root :url :version :dependencies] :in-any-order)) 26 | 27 | -------------------------------------------------------------------------------- /test/hydrox/core/regulator_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.core.regulator-test 2 | (:use midje.sweet) 3 | (:require [hydrox.core.regulator :refer :all] 4 | [clojure.java.io :as io])) 5 | 6 | ^{:refer hydrox.core.regulator/create-folio :added "0.1"} 7 | (fact "creates the folio for storing all the documentation information") 8 | 9 | ^{:refer hydrox.core.regulator/mount-folio :added "0.1"} 10 | (fact "adds a watcher to update function/test definitions when files in the project changes") 11 | 12 | ^{:refer hydrox.core.regulator/unmount-folio :added "0.1"} 13 | (fact "removes the file-change watcher") 14 | 15 | ^{:refer hydrox.core.regulator/init-folio :added "0.1"} 16 | (fact "runs through all the files and adds function/test definitions to the project") 17 | 18 | ^{:refer hydrox.core.regulator/start-regulator :added "0.1"} 19 | (fact "starts the regulator") 20 | 21 | ^{:refer hydrox.core.regulator/stop-regulator :added "0.1"} 22 | (fact "stops the regulator") 23 | 24 | ^{:refer hydrox.core.regulator/regulator :added "0.1"} 25 | (fact "creates a blank regulator, does not work") 26 | -------------------------------------------------------------------------------- /test/hydrox/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.core-test 2 | (:use midje.sweet) 3 | (:require [hydrox.core :refer :all] 4 | [hydrox.analyse :as analyser] 5 | [hydrox.common.util :as util] 6 | [hydrox.core.regulator :as regulator] 7 | [hara.component :as component] 8 | [clojure.java.io :as io])) 9 | 10 | ^{:refer hydrox.core/submerged? :added "0.1"} 11 | (fact "checks if dive has started") 12 | 13 | ^{:refer hydrox.core/single-use :added "0.1"} 14 | (fact "returns a working regulator for a given project file") 15 | 16 | ^{:refer hydrox.core/import-docstring :added "0.1"} 17 | (fact "imports docstrings given a regulator") 18 | 19 | ^{:refer hydrox.core/purge-docstring :added "0.1"} 20 | (fact "purges docstrings given a regulator") 21 | 22 | ^{:refer hydrox.core/generate-docs :added "0.1"} 23 | (fact "generates html docs for :documentation entries in project.clj") 24 | 25 | ^{:refer hydrox.core/dive :added "0.1"} 26 | (fact "starts a dive") 27 | 28 | ^{:refer hydrox.core/surface :added "0.1"} 29 | (fact "finishes a dive") 30 | 31 | 32 | (comment 33 | 34 | 35 | 36 | (event/deflistener log-listener 37 | :log 38 | m 39 | (println m)) 40 | 41 | (import-docstring) 42 | 43 | (dive) 44 | (surface) 45 | (purge-docstring) 46 | (import-docstring) 47 | *running* 48 | (watch/add (io/file (:root (read-project))) :small 49 | (fn [_ _ _ v] 50 | (println v))) 51 | oeu 52 | (watch/list (io/file (:root (read-project)))) 53 | (watch/remove (io/file (:root (read-project))) :hydrox) 54 | (doseq [reg *running*] 55 | (println reg) 56 | (import-docstring reg :all)) 57 | (mapv #(import-docstring % :all) *running*) 58 | ) 59 | 60 | (comment 61 | 62 | (component/start (regulator (read-project (io/file "../hara/project.clj")))) 63 | 64 | (def reg (let [proj (util/read-project) 65 | folio (-> proj 66 | (create-folio) 67 | (init-folio)) 68 | state (atom folio)] 69 | (Regulator. state proj))) 70 | 71 | (def reg (let [proj (-> (util/read-project) 72 | (assoc :initialise false)) 73 | folio (-> proj 74 | (regulator/create-folio) 75 | (analyser/add-file (io/file "src/hydrox/core/regulator.clj"))) 76 | state (atom folio)] 77 | (regulator/regulator state proj))) 78 | 79 | (-> reg :state deref :sink deref) 80 | 81 | (component/start reg) 82 | 83 | (dive) 84 | (surface) 85 | (generate-docs) 86 | 87 | (async/go 88 | (let [d (async/ regulator/*running* 92 | first 93 | :state 94 | deref 95 | :project 96 | :version) 97 | 98 | 99 | 100 | (:references @(:state reg)) 101 | 102 | (import-docstring (once-off "project.clj")) 103 | 104 | (import-docstring reg 'hydrox.doc.structure) 105 | (purge-docstring reg 'hydrox.analyse.test) 106 | 107 | @(:state reg) 108 | (:project reg) 109 | (.getParent(io/file "project.clj"))) 110 | -------------------------------------------------------------------------------- /test/hydrox/doc/checks_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.checks-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.checks :refer :all] 4 | [rewrite-clj.zip :as z])) 5 | 6 | ^{:refer hydrox.doc.checks/directive? :added "0.1"} 7 | (fact "checks if the element is a directive." 8 | 9 | (-> "[[:chapter {:title \"A Story\"}]]" 10 | z/of-string 11 | directive?) 12 | => true) 13 | 14 | ^{:refer hydrox.doc.checks/attribute? :added "0.1"} 15 | (fact "checks if the element is an attribute." 16 | 17 | (-> "[[{:title \"A Story\"}]]" 18 | z/of-string 19 | attribute?) 20 | => true) 21 | 22 | ^{:refer hydrox.doc.checks/code-directive? :added "0.1"} 23 | (fact "checks if the element is a code directive" 24 | 25 | (-> "[[:code {:type :javascript} 26 | \"1 + 1 == 2\"]]" 27 | z/of-string 28 | code-directive?) 29 | => true) 30 | 31 | ^{:refer hydrox.doc.checks/ns? :added "0.1"} 32 | (fact "checks if the element is a ns form" 33 | 34 | (-> "(ns ...)" 35 | z/of-string 36 | ns?) 37 | => true) 38 | 39 | ^{:refer hydrox.doc.checks/fact? :added "0.1"} 40 | (fact "checks if the element is a fact form" 41 | 42 | (-> "(fact ...)" 43 | z/of-string 44 | fact?) 45 | => true) 46 | 47 | ^{:refer hydrox.doc.checks/facts? :added "0.1"} 48 | (fact "checks if the element is a facts form" 49 | 50 | (-> "(facts ...)" 51 | z/of-string 52 | facts?) 53 | => true) 54 | 55 | ^{:refer hydrox.doc.checks/comment? :added "0.1"} 56 | (fact "checks if the element is a comment form") 57 | 58 | ^{:refer hydrox.doc.checks/paragraph? :added "0.1"} 59 | (fact "checks if the element is a paragraph (string)") 60 | 61 | ^{:refer hydrox.doc.checks/whitespace? :added "0.1"} 62 | (fact "checks if the element is a whitespace element") 63 | -------------------------------------------------------------------------------- /test/hydrox/doc/collect_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.collect-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.collect :refer :all])) 4 | 5 | ^{:refer hydrox.doc.collect/collect-namespaces :added "0.1"} 6 | (fact "combines `:ns-form` directives into a namespace map for easy referral" 7 | 8 | (collect-namespaces 9 | {:articles {"example" {:elements [{:type :ns-form 10 | :ns 'clojure.core}]}}} 11 | "example") 12 | => '{:articles {"example" {:elements ()}} 13 | :namespaces {clojure.core {:type :ns-form :ns clojure.core}}}) 14 | 15 | 16 | ^{:refer hydrox.doc.collect/collect-article :added "0.1"} 17 | (fact "shunts `:article` directives into a seperate `:meta` section" 18 | 19 | (collect-article 20 | {:articles {"example" {:elements [{:type :article 21 | :options {:color :light}}]}}} 22 | "example") 23 | => '{:articles {"example" {:elements [] 24 | :meta {:options {:color :light}}}}}) 25 | 26 | ^{:refer hydrox.doc.collect/collect-global :added "0.1"} 27 | (fact "shunts `:global` directives into a globally available `:meta` section" 28 | 29 | (collect-global 30 | {:articles {"example" {:elements [{:type :global 31 | :options {:color :light}}]}}} 32 | "example") 33 | => {:articles {"example" {:elements ()}} 34 | :meta {:options {:color :light}}}) 35 | 36 | ^{:refer hydrox.doc.collect/collect-tags :added "0.1"} 37 | (fact "puts any element with `:tag` attribute into a seperate `:tag` set" 38 | 39 | (collect-tags 40 | {:articles {"example" {:elements [{:type :chapter :tag "hello"} 41 | {:type :chapter :tag "world"}]}}} 42 | "example") 43 | => {:articles {"example" {:elements [{:type :chapter :tag "hello"} 44 | {:type :chapter :tag "world"}] 45 | :tags #{"hello" "world"}}}}) 46 | 47 | ^{:refer hydrox.doc.collect/collect-citations :added "0.1"} 48 | (fact "shunts `:citation` directives into a seperate `:citation` section" 49 | 50 | (collect-citations 51 | {:articles {"example" {:elements [{:type :citation :author "Chris"}]}}} 52 | "example") 53 | => {:articles {"example" {:elements [], 54 | :citations [{:type :citation, :author "Chris"}]}}}) 55 | -------------------------------------------------------------------------------- /test/hydrox/doc/link/anchors_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.anchors-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.link.anchors :refer :all])) 4 | 5 | ^{:refer hydrox.doc.link.anchors/link-anchors-lu :added "0.1"} 6 | (fact "creates the anchor lookup for tags and numbers" 7 | (-> {:articles {"example" {:elements [{:type :chapter :tag "hello" :number "1"}]}}} 8 | (link-anchors-lu "example") 9 | :anchors-lu) 10 | => {"example" {:by-number {:chapter {"1" {:type :chapter, :tag "hello", :number "1"}}}, 11 | :by-tag {"hello" {:type :chapter, :tag "hello", :number "1"}}}}) 12 | 13 | ^{:refer hydrox.doc.link.anchors/link-anchors :added "0.1"} 14 | (fact "creates a global anchors list based on the lookup" 15 | 16 | (-> {:articles {"example" {:elements [{:type :chapter :tag "hello" :number "1"}]}}} 17 | (link-anchors-lu "example") 18 | (link-anchors "example") 19 | :anchors) 20 | => {"example" {"hello" {:type :chapter, :tag "hello", :number "1"}}}) 21 | -------------------------------------------------------------------------------- /test/hydrox/doc/link/namespaces_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.namespaces-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.link.namespaces :refer :all])) 4 | 5 | ^{:refer hydrox.doc.link.namespaces/link-namespaces :added "0.1"} 6 | (fact "link elements with `:ns` forms to code" 7 | 8 | (link-namespaces 9 | {:articles {"example" {:elements [{:type :ns :ns "clojure.core"}]}} 10 | :namespaces {"clojure.core" {:code "(ns clojure.core)"}}} 11 | "example") 12 | => {:articles {"example" {:elements [{:type :code 13 | :ns "clojure.core" 14 | :origin :ns 15 | :indentation 0 16 | :code "(ns clojure.core)"}]}} 17 | :namespaces {"clojure.core" {:code "(ns clojure.core)"}}}) 18 | -------------------------------------------------------------------------------- /test/hydrox/doc/link/numbers_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.numbers-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.link.numbers :refer :all])) 4 | 5 | ^{:refer hydrox.doc.link.numbers/increment :added "0.1"} 6 | (fact "increments a string for alphanumerics and numbers" 7 | (increment "1") 8 | => "2" 9 | 10 | (increment "A") 11 | => "B") 12 | 13 | ^{:refer hydrox.doc.link.numbers/link-numbers-loop :added "0.1"} 14 | (fact "main loop logic for generation of numbers") 15 | 16 | ^{:refer hydrox.doc.link.numbers/link-numbers :added "0.1"} 17 | (fact "creates numbers for main sections, images, code and equations" 18 | (link-numbers {:articles {"example" {:elements [{:type :chapter :title "hello"} 19 | {:type :section :title "world"}]}}} 20 | "example") 21 | {:articles {"example" 22 | {:elements [{:type :chapter, :title "hello", :number "1"} 23 | {:type :section, :title "world", :number "1.1"}]}}}) 24 | -------------------------------------------------------------------------------- /test/hydrox/doc/link/references_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.references-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.link.references :refer :all] 4 | [rewrite-clj.zip :as z])) 5 | 6 | ^{:refer hydrox.doc.link.references/process-doc-nodes :added "0.1"} 7 | (fact "treat test nodes specially when rendering code" 8 | 9 | (->> (z/of-string "(+ 1 1) => (+ 2 2)") 10 | (iterate z/right*) 11 | (take-while identity) 12 | (map z/node) 13 | (process-doc-nodes)) 14 | => "(+ 1 1) => (+ 2 2)" 15 | ) 16 | 17 | ^{:refer hydrox.doc.link.references/link-references :added "0.1"} 18 | (fact "link code for elements to references" 19 | 20 | (link-references {:articles {"example" {:elements [{:type :reference :refer 'example.core/hello}]}} 21 | :references '{example.core {hello {:docs [] 22 | :source "(defn hello [] 1)"}}}} 23 | "example") 24 | => {:articles 25 | {"example" 26 | {:elements 27 | '[{:type :code, 28 | :refer example.core/hello, 29 | :origin :reference, 30 | :indentation 0, 31 | :code "(defn hello [] 1)", 32 | :mode :source, 33 | :title "source of example.core/hello"}]}}, 34 | :references '{example.core {hello {:docs [], :source "(defn hello [] 1)"}}}}) 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/hydrox/doc/link/stencil_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.stencil-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.link.stencil :refer :all])) 4 | 5 | ^{:refer hydrox.doc.link.stencil/transform-stencil :added "0.1"} 6 | (fact "takes a short-form and expands using anchor information" 7 | (transform-stencil "{{hello}}" "example" 8 | {"example" {"hello" {:number 1}}}) 9 | => "1" 10 | 11 | (transform-stencil "{{stuff/create}}" "example" 12 | {"stuff" {"create" {:number 2}}}) 13 | => "2" 14 | 15 | (transform-stencil "[[stuff/create]]" "example" 16 | {"stuff" {"create" {:number 2}}}) 17 | => "[2](stuff.html#create)" 18 | 19 | (transform-stencil "[[create]]" "example" 20 | {"example" {"create" {:number 2}}}) 21 | => "[2](#create)") 22 | 23 | 24 | ^{:refer hydrox.doc.link.stencil/link-stencil :added "0.1"} 25 | (fact "links extra information for using the stencil format" 26 | (link-stencil 27 | {:articles {"example" {:meta {:name "world"} 28 | :elements [{:type :paragraph 29 | :text "{{PROJECT.version}} {{DOCUMENT.name}}"} 30 | {:type :paragraph 31 | :text "{{hello}} {{example.hello.label}}"}]}} 32 | :project {:version "0.1"} 33 | :anchors {"example" {"hello" {:number 2 34 | :label "two"}}}} 35 | "example") 36 | => {:articles {"example" {:meta {:name "world"}, 37 | :elements [{:type :paragraph, :text "0.1 world"} 38 | {:type :paragraph, :text "2 two"}]}}, 39 | :project {:version "0.1"}, 40 | :anchors {"example" {"hello" {:number 2, :label "two"}}}}) 41 | -------------------------------------------------------------------------------- /test/hydrox/doc/link/tags_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.link.tags-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.link.tags :refer :all] 4 | [hydrox.doc.collect :as collect])) 5 | 6 | ^{:refer hydrox.doc.link.tags/inc-candidate :added "0.1"} 7 | (fact "creates an incremental version of a name" 8 | 9 | (inc-candidate "hello") => "hello-0" 10 | (inc-candidate "hello-1") => "hello-2") 11 | 12 | ^{:refer hydrox.doc.link.tags/tag-string :added "0.1"} 13 | (fact "creates a string that can be used as an anchor" 14 | 15 | (tag-string "hello.world/again") 16 | => "hello-world--again") 17 | 18 | ^{:refer hydrox.doc.link.tags/create-candidate :added "0.1"} 19 | (fact "creates a candidate tag from a variety of sources" 20 | 21 | (create-candidate {:origin :ns :ns 'clojure.core}) 22 | => "ns-clojure-core" 23 | 24 | (create-candidate {:title "hello again"}) 25 | => "hello-again" 26 | 27 | (create-candidate {:type :image :src "http://github.com/hello/gather.jpeg"}) 28 | => "img-http----github-com--hello--gather-jpeg") 29 | 30 | ^{:refer hydrox.doc.link.tags/create-tag :added "0.1"} 31 | (fact "creates a tag from an element" 32 | 33 | (let [tags (atom #{}) 34 | result (create-tag {:title "hello"} tags)] 35 | [@tags result]) 36 | => [#{"hello"} {:title "hello", :tag "hello"}] 37 | 38 | (let [tags (atom #{"hello"}) 39 | result (create-tag {:title "hello"} tags)] 40 | [@tags result]) 41 | => [#{"hello" "hello-0"} {:title "hello", :tag "hello-0"}]) 42 | 43 | ^{:refer hydrox.doc.link.tags/link-tags :added "0.1"} 44 | (fact "creates a tag for elements within the article" 45 | (-> {:articles {"example" {:elements [{:type :chapter :title "hello world"} 46 | {:type :chapter :title "hello world"}]}}} 47 | (collect/collect-tags "example") 48 | (link-tags "example")) 49 | => {:articles 50 | {"example" 51 | {:elements [{:type :chapter, :title "hello world", :tag "hello-world"} 52 | {:type :chapter, :title "hello world", :tag "hello-world-0"}], 53 | :tags #{}}}}) 54 | -------------------------------------------------------------------------------- /test/hydrox/doc/parse_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.parse-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.parse :refer :all] 4 | [rewrite-clj.zip :as z])) 5 | 6 | ^{:refer hydrox.doc.parse/parse-ns-form :added "0.1"} 7 | (fact "converts a ns zipper into an element" 8 | 9 | (-> (z/of-string "(ns example.core)") 10 | (parse-ns-form)) 11 | => '{:type :ns-form 12 | :indentation 0 13 | :ns example.core 14 | :code "(ns example.core)"}) 15 | 16 | ^{:refer hydrox.doc.parse/code-form :added "0.1"} 17 | (fact "converts a form zipper into a code string" 18 | 19 | (-> (z/of-string "(fact (+ 1 1) \n => 2)") 20 | (code-form 'fact)) 21 | => "(+ 1 1) \n => 2") 22 | 23 | ^{:refer hydrox.doc.parse/parse-fact-form :added "0.1"} 24 | (fact "convert a fact zipper into an element" 25 | 26 | (-> (z/of-string "(fact (+ 1 1) \n => 2)") 27 | (parse-fact-form)) 28 | => {:type :block :indentation 2 :code "(+ 1 1) \n => 2"}) 29 | 30 | ^{:refer hydrox.doc.parse/parse-comment-form :added "0.1"} 31 | (fact "convert a comment zipper into an element" 32 | 33 | (-> (z/of-string "(comment (+ 1 1) \n => 2)") 34 | (parse-comment-form)) 35 | => {:type :block :indentation 2 :code "(+ 1 1) \n => 2"}) 36 | 37 | ^{:refer hydrox.doc.parse/parse-paragraph :added "0.1"} 38 | (fact "converts a string zipper into an element" 39 | (-> (z/of-string "\"this is a paragraph\"") 40 | (parse-paragraph)) 41 | => {:type :paragraph :text "this is a paragraph"}) 42 | 43 | ^{:refer hydrox.doc.parse/parse-directive :added "0.1"} 44 | (fact "converts a directive zipper into an element" 45 | (-> (z/of-string "[[:chapter {:title \"hello world\"}]]") 46 | (parse-directive)) 47 | => {:type :chapter :title "hello world"} 48 | 49 | (binding [*namespace* 'example.core] 50 | (-> (z/of-string "[[:ns {:title \"hello world\"}]]") 51 | (parse-directive))) 52 | => {:type :ns, :title "hello world", :ns 'example.core}) 53 | 54 | ^{:refer hydrox.doc.parse/parse-attribute :added "0.1"} 55 | (fact "coverts an attribute zipper into an element" 56 | (-> (z/of-string "[[{:title \"hello world\"}]]") 57 | (parse-attribute)) 58 | => {:type :attribute, :title "hello world"}) 59 | 60 | ^{:refer hydrox.doc.parse/parse-code-directive :added "0.1"} 61 | (fact "coverts an code directive zipper into an element" 62 | (-> (z/of-string "[[:code {:language :ruby} \"1 + 1 == 2\"]]") 63 | (parse-code-directive)) 64 | => {:type :block, :indentation 0 :code "1 + 1 == 2" :language :ruby}) 65 | 66 | ^{:refer hydrox.doc.parse/parse-whitespace :added "0.1"} 67 | (fact "coverts a whitespace zipper into an element" 68 | (-> (z/of-string "1 2 3") 69 | (z/right*) 70 | (parse-whitespace)) 71 | => {:type :whitespace, :code [" "]}) 72 | 73 | ^{:refer hydrox.doc.parse/parse-code :added "0.1"} 74 | (fact "coverts a code zipper into an element" 75 | (-> (z/of-string "(+ 1 1) (+ 2 2)") 76 | (parse-code)) 77 | => {:type :code, :indentation 0, :code ["(+ 1 1)"]}) 78 | 79 | ^{:refer hydrox.doc.parse/parse-loop :added "0.1"} 80 | (fact "the main loop for the parser" 81 | (-> (z/of-string "(ns example.core) 82 | [[:chapter {:title \"hello\"}]] 83 | (+ 1 1) 84 | (+ 2 2)") 85 | (parse-loop {})) 86 | => [{:type :ns-form, :indentation 0, :ns 'example.core, :code "(ns example.core)"} 87 | {:type :chapter, :title "hello"} 88 | {:type :code, :indentation 0, :code ["(+ 1 1)" 89 | " " 90 | "\n" 91 | " " 92 | "(+ 2 2)"]}]) 93 | -------------------------------------------------------------------------------- /test/hydrox/doc/render/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render.util-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.render.util :refer :all])) 4 | 5 | ^{:refer hydrox.doc.render.util/adjust-indent :added "0.1"} 6 | (fact "fixes indentation for code that is off slightly due to alignment" 7 | 8 | (adjust-indent "(+ 1\n 2)" 2) 9 | => "(+ 1\n 2)") 10 | 11 | ^{:refer hydrox.doc.render.util/basic-html-escape :added "0.1"} 12 | (fact "escapes characters using standard html format" 13 | 14 | (basic-html-escape "<>") 15 | => "<>") 16 | 17 | ^{:refer hydrox.doc.render.util/basic-html-unescape :added "0.1"} 18 | (fact "unescapes characters with standard html format" 19 | 20 | (basic-html-unescape "&quot;") 21 | => """) 22 | 23 | ^{:refer hydrox.doc.render.util/markup :added "0.1"} 24 | (fact "calls the markdown library to create markup from a string") 25 | 26 | 27 | ^{:refer hydrox.doc.render.util/join :added "0.1"} 28 | (fact "like string/join but will return the input if it is a string" 29 | 30 | (join "hello") => "hello" 31 | 32 | (join ["hello" " " "world"]) => "hello world") 33 | -------------------------------------------------------------------------------- /test/hydrox/doc/render_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.render-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.render :refer :all])) 4 | -------------------------------------------------------------------------------- /test/hydrox/doc/structure_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc.structure-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc.structure :refer :all])) 4 | 5 | ^{:refer hydrox.doc.structure/inclusive :added "0.1"} 6 | (fact "determines which sections are contained by the other" 7 | (inclusive :article :section) => true 8 | 9 | (inclusive :chapter :subsection) => true 10 | 11 | (inclusive :chapter :chapter) => false 12 | 13 | (inclusive :section :chapter) => false) 14 | 15 | ^{:refer hydrox.doc.structure/seperate :added "0.1"} 16 | (fact "groups elements in an array " 17 | (seperate #(= 1 %) [1 2 2 1 3 4 5]) 18 | => [[1 2 2] [1 3 4 5]]) 19 | 20 | ^{:refer hydrox.doc.structure/containify :added "0.1"} 21 | (fact "makes a nested vector object from a sequence of elements" 22 | (containify [{:type :generic} 23 | {:type :paragraph} 24 | {:type :chapter} 25 | {:type :paragraph} 26 | {:type :section} 27 | {:type :paragraph} 28 | {:type :subsection} 29 | {:type :paragraph} 30 | {:type :section} 31 | {:type :chapter} 32 | {:type :section} 33 | {:type :appendix}]) 34 | => [{:type :article} 35 | [{:type :generic} 36 | [{:type :paragraph}]] 37 | [{:type :chapter} 38 | [{:type :paragraph}] 39 | [{:type :section} 40 | [{:type :paragraph}] 41 | [{:type :subsection} 42 | [{:type :paragraph}]]] 43 | [{:type :section}]] 44 | [{:type :chapter} 45 | [{:type :section}]] 46 | [{:type :appendix}]]) 47 | 48 | ^{:refer hydrox.doc.structure/structure :added "0.1"} 49 | (fact "creates a nested map structure of elements and their containers" 50 | (structure [{:type :generic} 51 | {:type :paragraph} 52 | {:type :chapter} 53 | {:type :paragraph} 54 | {:type :section} 55 | {:type :paragraph} 56 | {:type :subsection} 57 | {:type :paragraph} 58 | {:type :section} 59 | {:type :chapter} 60 | {:type :section} 61 | {:type :appendix}]) 62 | => {:type :article, 63 | :elements [{:type :generic, 64 | :elements [{:type :paragraph}]} 65 | {:type :chapter, 66 | :elements [{:type :paragraph} 67 | {:type :section, 68 | :elements [{:type :paragraph} 69 | {:type :subsection, 70 | :elements [{:type :paragraph}]}]} 71 | {:type :section, 72 | :elements []}]} 73 | {:type :chapter, 74 | :elements [{:type :section, :elements []}]} 75 | {:type :appendix, :elements []}]}) 76 | -------------------------------------------------------------------------------- /test/hydrox/doc_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.doc-test 2 | (:use midje.sweet) 3 | (:require [hydrox.doc :refer :all] 4 | [hydrox.analyse :as analyser])) 5 | 6 | ^{:refer hydrox.doc/prepare-article :added "0.1"} 7 | (fact "generates the flat outline for rendering") 8 | 9 | ^{:refer hydrox.doc/generate :added "0.1"} 10 | (fact "generates the tree outline for rendering") 11 | 12 | ^{:refer hydrox.doc/prepare-includes :added "0.1"} 13 | (fact "prepare template accept includes") 14 | 15 | ^{:refer hydrox.doc/find-includes :added "0.1"} 16 | (fact "finds elements with `@=` tags" 17 | 18 | (find-includes "<@=hello> <@=world>") 19 | => #{:hello :world}) 20 | 21 | ^{:refer hydrox.doc/render-entry :added "0.1"} 22 | (fact "helper function that is called by both render-single and render-all") 23 | 24 | ^{:refer hydrox.doc/copy-files :added "0.1"} 25 | (fact "copies all files from the template directory into the output directory") 26 | 27 | ^{:refer hydrox.doc/render-single :added "0.1"} 28 | (fact "render for a single entry in the project.clj map") 29 | 30 | ^{:refer hydrox.doc/render-all :added "0.1"} 31 | (fact "render for all documentation entries in the project.clj map") 32 | 33 | (comment 34 | 35 | (use 'hydrox.core) 36 | 37 | (def reg (single-use "../../chit/hara/project.clj")) 38 | (swap! (:state reg) #(analyser/add-file % (clojure.java.io/file "../../chit/hara/test/hara/group_test.clj"))) 39 | 40 | 41 | (keys @(:state reg)) 42 | (:meta :articles :namespaces :project :references :registry :root :namespace-lu) 43 | (-> @(:state reg) :references (get-in ['hara.group 'defgroup])) 44 | (import-docstring reg) 45 | (import-docstring reg 'hara.group) 46 | (generate-docs ) 47 | 48 | 49 | (-> (prepare-article 50 | (-> hydrox.core.regulator/*running* 51 | first 52 | :state 53 | deref) 54 | "sample-document" 55 | "test/documentation/sample_document.clj") 56 | :anchors-lu 57 | (get-in [:articles "sample-document" :elements]))) 58 | -------------------------------------------------------------------------------- /test/hydrox/meta/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.meta.util-test 2 | (:use midje.sweet) 3 | (:require [hydrox.meta.util :refer :all] 4 | [clojure.java.io :as io] 5 | [rewrite-clj.zip :as z] 6 | [rewrite-clj.node :as node])) 7 | 8 | ^{:refer hydrox.meta.util/append-node :added "0.1"} 9 | (fact "Adds node as well as whitespace and newline on right" 10 | 11 | (-> (z/of-string "(+)") 12 | (z/down) 13 | (append-node 2) 14 | (append-node 1) 15 | (z/->root-string)) 16 | => "(+\n 1\n 2)") 17 | 18 | ^{:refer hydrox.meta.util/has-quotes? :added "0.1"} 19 | (fact "checks if a string has quotes" 20 | 21 | (has-quotes? "\"hello\"") 22 | => true) 23 | 24 | ^{:refer hydrox.meta.util/strip-quotes :added "0.1"} 25 | (fact "gets rid of quotes in a string" 26 | 27 | (strip-quotes "\"hello\"") 28 | => "hello") 29 | 30 | ^{:refer hydrox.meta.util/escape-newlines :added "0.1"} 31 | (fact "makes sure that newlines are printable" 32 | 33 | (escape-newlines "\\n") 34 | => "\\n") 35 | 36 | ^{:refer hydrox.meta.util/escape-escapes :added "0.1"} 37 | (fact "makes sure that newlines are printable" 38 | 39 | (escape-escapes "\\n") 40 | => "\\\\n") 41 | 42 | ^{:refer hydrox.meta.util/escape-quotes :added "0.1"} 43 | (fact "makes sure that quotes are printable in string form" 44 | 45 | (escape-quotes "\"hello\"") 46 | => "\\\"hello\\\"") 47 | 48 | ^{:refer hydrox.meta.util/strip-quotes-array :added "0.1"} 49 | (fact "utility that strips quotes when not the result of a fact" 50 | (strip-quotes-array ["\"hello\""]) 51 | => ["hello"] 52 | 53 | (strip-quotes-array ["(str \"hello\")" " " "=>" " " "\"hello\""]) 54 | => ["(str \"hello\")" " " "=>" " " "\"hello\""]) 55 | 56 | 57 | ^{:refer hydrox.meta.util/nodes->docstring :added "0.1"} 58 | (fact "converts nodes to a docstring compatible" 59 | (->> (z/of-string "\"hello\"\n (+ 1 2)\n => 3 ") 60 | (iterate z/right*) 61 | (take-while identity) 62 | (map z/node) 63 | (nodes->docstring) 64 | (node/string)) 65 | => "\"hello\n (+ 1 2)\n => 3 \"" 66 | 67 | (->> (z/of-string (str [\e \d])) 68 | (iterate z/right*) 69 | (take-while identity) 70 | (map z/node) 71 | (nodes->docstring) 72 | (str) 73 | (read-string)) 74 | => "[\\e \\d]") 75 | 76 | ^{:refer hydrox.meta.util/import-location :added "0.1"} 77 | (fact "imports the meta information and docstring") 78 | 79 | ^{:refer hydrox.meta.util/write-to-file :added "0.1"} 80 | (fact "exports the zipper contents to file") 81 | 82 | ^{:refer hydrox.meta.util/all-files :added "0.1"} 83 | (fact "finds all files in the project given a context" 84 | 85 | (->> (all-files {:root (.getCanonicalPath (io/file "example"))} :root "md") 86 | (map #(.getName %))) 87 | => ["README.md"]) 88 | -------------------------------------------------------------------------------- /test/hydrox/meta_test.clj: -------------------------------------------------------------------------------- 1 | (ns hydrox.meta-test 2 | (:use midje.sweet) 3 | (:require [hydrox.meta :refer :all])) 4 | 5 | ^{:refer hydrox.meta/selector :added "0.1"} 6 | (fact "builds a selector for functions" 7 | 8 | (selector 'hello) 9 | => '[(#{defn defmacro defmulti} | hello ^:%?- string? ^:%?- map? & _)]) 10 | 11 | ^{:refer hydrox.meta/edit-file :added "0.1"} 12 | (fact "helper function for file manipulation used by import and purge") 13 | 14 | ^{:refer hydrox.meta/import-fn :added "0.1"} 15 | (fact "helper function for file import") 16 | 17 | ^{:refer hydrox.meta/import-var :added "0.1"} 18 | (fact "import docs for a single var") 19 | 20 | ^{:refer hydrox.meta/import-file :added "0.1"} 21 | (fact "import docs for a file") 22 | 23 | ^{:refer hydrox.meta/import-project :added "0.1"} 24 | (fact "import docs for the entire project") 25 | 26 | ^{:refer hydrox.meta/purge-fn :added "0.1"} 27 | (fact "helper function for file purge") 28 | 29 | ^{:refer hydrox.meta/purge-var :added "0.1"} 30 | (fact "purge docs for a single var") 31 | 32 | ^{:refer hydrox.meta/purge-file :added "0.1"} 33 | (fact "purge docs for a file") 34 | 35 | ^{:refer hydrox.meta/purge-project :added "0.1"} 36 | (fact "purge docs for the entire project") 37 | 38 | 39 | (comment 40 | (purge-file "src/hydrox/code.clj") 41 | 42 | (import-file "src/hydrox/code.clj" 43 | {'hydrox.meta {'import-var {:docs [(node/string-node "Hello there")] 44 | :meta {:added "0.1"}}}}) 45 | (import-var "src/hydrox/code.clj" 46 | 'import-var 47 | {'hydrox.meta {'import-var {:docs [(node/string-node "Hello there")] 48 | :meta {:added "0.1"}}}}) 49 | 50 | (def z (source/of-file "src/hydrox/code.clj"))) 51 | --------------------------------------------------------------------------------