diff --git a/.gitignore b/.gitignore index 5665fdc9d..34b981aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,17 @@ -uberdoc.html -.DS_Store .cake -pom.xml +.DS_Store +.lein-deps-sum +.lein-failures +.nrepl-port *jar -lib +/.calva/output-window/ +/.clj-kondo/.cache +/.lsp/.cache +/.portal/vs-code.edn +/site/uberdoc.html classes +lib out -webgen.cache -.lein-failures -.lein-deps-sum -docs/*.html +pom.xml target -.nrepl-port +webgen.cache diff --git a/docs/uberdoc.html b/docs/uberdoc.html new file mode 100644 index 000000000..b5747fcb6 --- /dev/null +++ b/docs/uberdoc.html @@ -0,0 +1,3749 @@ + +
dependencies
| (this space intentionally left almost blank) | ||||||||||||||||||
A new way to think about programs+ +What if your code and its documentation were one and the same? + +Much of the philosophy guiding literate programming is the realization of the answer to this question. +However, if literate programming stands as a comprehensive programming methodology at one of end of the +spectrum and no documentation stands as its antithesis, then Marginalia falls somewhere between. That is, +you should always aim for comprehensive documentation, but the shortest path to a useful subset is the +commented source code itself. + +The art of Marginalia+ +If you’re fervently writing code that is heavily documented, then using Marginalia for your Clojure projects +is as simple as running it on your codebase. However, if you’re unaccustomed to documenting your source, then +the guidelines herein will help you make the most out of Marginalia for true-power documentation. + +Following the guidelines will work to make your code not only easier to follow: it will make it better. +The very process of using Marginalia will help to crystallize your understanding of problem and its solution(s). + +The quality of the prose in your documentation will often reflect the quality of the code itself thus highlighting +problem areas. The elimination of problem areas will solidify your code and its accompanying prose. Marginalia +provides a virtuous circle spiraling inward toward maximal code quality. + +The one true way+ +
| (ns marginalia.core + (:require + [clojure.java.io :as io] + [clojure.string :as str] + [clojure.tools.cli :refer [cli]] + [marginalia.html :refer [uberdoc-html index-html single-page-html]] + [marginalia.parser :refer [parse-file parse-ns *lift-inline-comments* *delete-lifted-comments*]]) + (:import + (java.io File FileReader))) | ||||||||||||||||||
+ | (set! *warn-on-reflection* true) | ||||||||||||||||||
File System Utilities+ | |||||||||||||||||||
Performs roughly the same task as the UNIX | (defn ls + [path] + (let [file (io/file path)] + (if (.isDirectory file) + (seq (.list file)) + (when (.exists file) + [path])))) | ||||||||||||||||||
+ | (defn mkdir [path] + (.mkdirs (io/file path))) | ||||||||||||||||||
Ensure that the directory specified by | (defn ensure-directory! + [path] + (when-not (ls path) + (mkdir path))) | ||||||||||||||||||
Many Marginalia fns use dir? to recursively search a filepath. + | (defn dir? + [path] + (.isDirectory (io/file path))) | ||||||||||||||||||
Returns a string containing the files extension. + | (defn find-file-extension + [^File file] + (second (re-find #"\.([^.]+)$" (.getName file)))) | ||||||||||||||||||
Predicate. Returns true for "normal" files with a file extension which +passes the provided predicate. + | (defn processable-file? + [pred ^File file] + (when (.isFile file) + (-> file find-file-extension pred))) | ||||||||||||||||||
Returns a seq of processable file paths (strings) in alphabetical order by +namespace. + | (defn find-processable-file-paths + [dir pred] + (->> (io/file dir) + (file-seq) + (filter (partial processable-file? pred)) + (sort-by parse-ns) + (map #(.getCanonicalPath ^File %)))) | ||||||||||||||||||
Project Info Parsing+ +Marginalia will parse info out of your project.clj to display in +the generated html file's header. + | |||||||||||||||||||
Parses a project.clj file and returns a map in the following form + +
+
+by merging into the name and version information the rest of the defproject
+forms ( | (defn parse-project-form + [[_ project-name version-number & attributes]] + (merge {:name (str project-name) + :version version-number} + (apply hash-map attributes))) | ||||||||||||||||||
Parses a project file -- './project.clj' by default -- and returns a map + assembled according to the logic in parse-project-form. + | (defn parse-project-file + ([] (parse-project-file "./project.clj")) + ([path] + (try + (let [rdr (clojure.lang.LineNumberingPushbackReader. + (FileReader. + (io/file path)))] + (loop [line (read rdr)] + (let [found-project? (= 'defproject (first line))] + (if found-project? + (parse-project-form line) + (recur (read rdr)))))) + (catch Exception e + (throw (Exception. + (str + "There was a problem reading the project definition from " + path))))))) | ||||||||||||||||||
Source File Analysis+ | |||||||||||||||||||
TODO: why are these args unused? + | (defn end-of-block? [_cur-group _groups lines] + (let [line (first lines) + next-line (second lines) + next-line-code (get next-line :code-text )] + (when (or (and (:code-text line) + (:docs-text next-line)) + (re-find #"^\(def" (str/trim next-line-code))) + true))) | ||||||||||||||||||
+ | (defn merge-line [line m] + (cond + (:docstring-text line) (assoc m + :docs + (conj (get m :docs []) line)) + (:code-text line) (assoc m + :codes + (conj (get m :codes []) line)) + (:docs-text line) (assoc m + :docs + (conj (get m :docs []) line)))) | ||||||||||||||||||
+ | (defn group-lines [doc-lines] + (loop [cur-group {} + groups [] + lines doc-lines] + (cond + (empty? lines) (conj groups cur-group) + (end-of-block? cur-group groups lines) + (recur (merge-line (first lines) {}) (conj groups cur-group) (rest lines)) + :else (recur (merge-line (first lines) cur-group) groups (rest lines))))) | ||||||||||||||||||
+ | (defn path-to-doc [filename] + {:ns (parse-ns (io/file filename)) + :groups (parse-file filename)}) | ||||||||||||||||||
Output Generation+ | |||||||||||||||||||
+ | (defn filename-contents + [props output-dir all-files parsed-file] + {:name (io/file output-dir (str (:ns parsed-file) ".html")) + :contents (single-page-html props parsed-file all-files)}) | ||||||||||||||||||
+ | (defn multidoc! + [output-dir files-to-analyze props] + (let [parsed-files (map path-to-doc files-to-analyze) + index (index-html props parsed-files) + pages (map #(filename-contents props output-dir parsed-files %) parsed-files)] + (doseq [f (conj pages {:name (io/file output-dir "toc.html") + :contents index})] + (spit (:name f) (:contents f))))) | ||||||||||||||||||
Generates an uberdoc html file from 3 pieces of information: + +
| (defn uberdoc! + [output-file-name files-to-analyze props] + (let [source (uberdoc-html + props + (map path-to-doc files-to-analyze))] + (spit output-file-name source))) | ||||||||||||||||||
External Interface (command-line, lein, cake, etc)+ | |||||||||||||||||||
These functions support Marginalia's use by client software or command-line +users. + | |||||||||||||||||||
+ | (def ^:private file-extensions #{"clj" "cljs" "cljx" "cljc"}) | ||||||||||||||||||
Given a collection of filepaths, returns a lazy sequence of filepaths to all + .clj, .cljs, .cljx, and .cljc files on those paths: directory paths will be searched + recursively for files. + | (defn format-sources + [sources] + (if (nil? sources) + (find-processable-file-paths "./src" file-extensions) + (->> sources + (mapcat #(if (dir? %) + (find-processable-file-paths % file-extensions) + [(.getCanonicalPath (io/file %))]))))) | ||||||||||||||||||
+ | (defn split-deps [deps] + (when deps + (for [d (str/split deps #";") + :let [[group artifact version] (str/split d #":")]] + [(if (= group artifact) artifact (str group "/" artifact)) + version]))) | ||||||||||||||||||
Check if a source file is excluded from the generated documentation + | (defn source-excluded? + [source opts] + (if-not (empty? + (filter #(if (re-find (re-pattern %) source) + true + false) + (-> opts :marginalia :exclude))) + true + false)) | ||||||||||||||||||
Default generation: given a collection of filepaths in a project, find the .clj + files at these paths and, if Clojure source files are found: + +
| (defn run-marginalia + [args & [project]] + (let [[{:keys [dir file name version desc deps css js multi + leiningen exclude + lift-inline-comments exclude-lifted-comments]} files help] + (cli args + ["-d" "--dir" + "Directory into which the documentation will be written" :default "./docs"] + ["-f" "--file" + "File into which the documentation will be written" :default "uberdoc.html"] + ["-n" "--name" + "Project name - if not given will be taken from project.clj"] + ["-v" "--version" + "Project version - if not given will be taken from project.clj"] + ["-D" "--desc" + "Project description - if not given will be taken from project.clj"] + ["-a" "--deps" + "Project dependencies in the form <group1>:<artifact1>:<version1>;<group2>... + If not given will be taken from project.clj"] + ["-c" "--css" + "Additional css resources <resource1>;<resource2>;... + If not given will be taken from project.clj."] + ["-j" "--js" + "Additional javascript resources <resource1>;<resource2>;... + If not given will be taken from project.clj"] + ["-m" "--multi" + "Generate each namespace documentation as a separate file" :flag true] + ["-l" "--leiningen" + "Generate the documentation for a Leiningen project file."] + ["-e" "--exclude" + "Exclude source file(s) from the document generation process <file1>;<file2>;... + If not given will be taken from project.clj"] + ["-L" "--lift-inline-comments" + "Lift ;; inline comments to the top of the enclosing form. + They will be treated as if they preceded the enclosing form." :flag true] + ["-X" "--exclude-lifted-comments" + "If ;; inline comments are being lifted into documentation + then also exclude them from the source code display." :flag true]) + sources (distinct (format-sources (seq files))) + sources (if leiningen (cons leiningen sources) sources)] + (if-not sources + (do + (println "Wrong number of arguments passed to Marginalia.") + (println help)) + (binding [*lift-inline-comments* lift-inline-comments + *delete-lifted-comments* exclude-lifted-comments] + (let [project-clj (or project + (when (.exists (io/file "project.clj")) + (parse-project-file))) + choose #(or %1 %2) + marg-opts (merge-with choose + {:css (when css (str/split css #";")) + :javascript (when js (str/split js #";")) + :exclude (when exclude (str/split exclude #";")) + :leiningen leiningen} + (:marginalia project-clj)) + opts (merge-with choose + {:name name + :version version + :description desc + :dependencies (split-deps deps) + :multi multi + :marginalia marg-opts} + project-clj) + sources (->> sources + (filter #(not (source-excluded? % opts))) + (into []))] + (println "Generating Marginalia documentation for the following source files:") + (doseq [s sources] + (println " " s)) + (println) + (ensure-directory! dir) + (if multi + (multidoc! dir sources opts) + (uberdoc! (str dir "/" file) sources opts)) + (println "Done generating your documentation in" dir) + (println "")))))) | ||||||||||||||||||
Utilities for converting parse results into html. + | (ns marginalia.html + (:use [marginalia.hiccup :only (html escape-html)]) + (:require [clojure.string :as str]) + (:import [com.petebevin.markdown MarkdownProcessor])) | ||||||||||||||||||
+ | (def ^{:dynamic true} *resources* "./vendor/") | ||||||||||||||||||
+ | (defn css-rule [rule] + (let [sels (reverse (rest (reverse rule))) + props (last rule)] + (str (apply str (interpose " " (map name sels))) + "{" (apply str (map #(str (name (key %)) ":" (val %) ";") props)) "}"))) | ||||||||||||||||||
Quick and dirty dsl for inline css rules, similar to hiccup. + + ex. -> | (defn css + [& rules] + (html [:style {:type "text/css"} + (apply str (map css-rule rules))])) | ||||||||||||||||||
Stolen from leiningen + | (defn slurp-resource + [resource-name] + (try + (-> (.getContextClassLoader (Thread/currentThread)) + (.getResourceAsStream resource-name) + (java.io.InputStreamReader.) + (slurp)) + (catch java.lang.NullPointerException npe + (println (str "Could not locate resources at " resource-name)) + (println " ... attempting to fix.") + (let [resource-name (str *resources* resource-name)] + (try + (-> (.getContextClassLoader (Thread/currentThread)) + (.getResourceAsStream resource-name) + (java.io.InputStreamReader.) + (slurp)) + (catch java.lang.NullPointerException npe + (println (str " STILL could not locate resources at " resource-name ". Giving up!")))))))) | ||||||||||||||||||
+ | (defn inline-js [resource] + (let [src (slurp-resource resource)] + (html [:script {:type "text/javascript"} + src]))) | ||||||||||||||||||
+ | (defn inline-css [resource] + (let [src (slurp-resource resource)] + (html [:style {:type "text/css"} + (slurp-resource resource)]))) | ||||||||||||||||||
The following functions handle preparation of doc text (both comment and docstring +based) for display through html & css. + | |||||||||||||||||||
Markdown processor. + | (def mdp (com.petebevin.markdown.MarkdownProcessor.)) | ||||||||||||||||||
Markdown string to html converter. Translates strings like: + + "# header!" -> "## header!" -> ... + | (defn md + [s] + (.markdown mdp s)) | ||||||||||||||||||
As a result of docifying then grouping, you'll end up with a seq like this one: + +
+
+
| |||||||||||||||||||
Converts a docs section to html by threading each doc line through the forms + outlined above. + +ex. (docs-to-html [{:doc-text "# hello world!"} {:docstring-text "I'm a docstring!}]) + + -> | (defn docs-to-html + [docs] + (-> docs + str + (md))) | ||||||||||||||||||
+ | (defn codes-to-html [code-block] + (html [:pre {:class "brush: clojure"} + (escape-html code-block)])) | ||||||||||||||||||
+ | (defn section-to-html [section] + (html [:tr + [:td {:class "docs"} (docs-to-html + (if (= (:type section) :comment) + (:raw section) + (:docstring section)))] + [:td {:class "codes"} (if (= (:type section) :code) + (codes-to-html (:raw section)))]])) | ||||||||||||||||||
+ | (defn dependencies-html [deps & header-name] + (when-let [deps (seq deps)] + (let [header-name (or header-name "dependencies")] + (html [:div {:class "dependencies"} + [:h3 header-name] + [:table + (map #(html [:tr + [:td {:class "dep-name"} (str (first %))] + [:td {:class "dotted"} [:hr]] + [:td {:class "dep-version"} (second %)]]) + deps)]])))) | ||||||||||||||||||
Load Optional Resources+ +Use external Javascript and CSS in your documentation. For example: +To format Latex math equations, download the +MathJax Javascript library to the docs +directory and then add + +
+
+to project.clj. Below is a simple example of both inline and block +formatted equations. + +Optionally, you can put the MathJax CDN URL directly as a value of
+
+That way you won't have to download and carry around the MathJax library. + +When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\) and they are +$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ + | |||||||||||||||||||
Generate script and link tags for optional external javascript and css. + | (defn opt-resources-html + [project-info] + (let [options (:marginalia project-info) + javascript (:javascript options) + css (:css options)] + (html (concat + (when javascript + (map #(vector :script {:type "text/javascript" :src %}) javascript)) + (when css + (map #(vector :link {:tyle "text/css" :rel "stylesheet" :href %}) css)))))) | ||||||||||||||||||
Is <h1/> overloaded? Maybe we should consider redistributing +header numbers instead of adding classes to all the h1 tags. + | (defn header-html [project-info] + (html + [:tr + [:td {:class "docs"} + [:div {:class "header"} + [:h1 {:class "project-name"} (if (seq (:url project-info)) + [:a {:href (:url project-info)} (:name project-info)] + (:name project-info))] + [:h2 {:class "project-version"} (:version project-info)] + [:br] + (md (:description project-info))] + (dependencies-html (:dependencies project-info)) + (dependencies-html (:dev-dependencies project-info) "dev dependencies")] + [:td {:class "codes" + :style "text-align: center; vertical-align: middle;color: #666;padding-right:20px"} + [:br] + [:br] + [:br] + "(this space intentionally left almost blank)"]])) | ||||||||||||||||||
Creates an 'a' tag pointing to the | (defn link-to-namespace + [namespace-name anchor? & attrs] + [:a (into {:href (if anchor? + (str "#" namespace-name) + (str namespace-name ".html"))} + attrs) + namespace-name]) | ||||||||||||||||||
This is a hack, as in the case when | (defn link-to-toc + [anchor?] + (link-to-namespace "toc" anchor? {:class "toc-link"})) | ||||||||||||||||||
+ | (defn toc-html [props docs] + (html + [:tr + [:td {:class "docs"} + [:div {:class "toc"} + [:a {:name "toc"} [:h3 "namespaces"]] + [:ul + (map #(vector :li (link-to-namespace (:ns %) (:uberdoc? props))) + docs)]]] + [:td {:class "codes"} " "]])) | ||||||||||||||||||
+ | (defn floating-toc-html [docs] + [:div {:id "floating-toc"} + [:ul + (map #(vector :li {:class "floating-toc-li" + :id (str "floating-toc_" (:ns %))} + (:ns %)) + docs)]]) | ||||||||||||||||||
+ | (defn groups-html [props doc] + (html + [:tr + [:td {:class "docs"} + [:div {:class "docs-header"} + [:a {:class "anchor" :name (:ns doc) :href (str "#" (:ns doc))} + [:h1 {:class "project-name"} + (:ns doc)] + (link-to-toc (:uberdoc? props))]]] + [:td {:class "codes"}]] + (map section-to-html (:groups doc)) + [:tr + [:td {:class "spacer docs"} " "] + [:td {:class "codes"}]])) | ||||||||||||||||||
+ | (def reset-css + (css [:html {:margin 0 :padding 0}] + [:h1 {:margin 0 :padding 0}] + [:h2 {:margin 0 :padding 0}] + [:h3 {:margin 0 :padding 0}] + [:h4 {:margin 0 :padding 0}] + [:a {:color "#261A3B"}] + [:a:visited {:color "#261A3B"}])) | ||||||||||||||||||
+ | (def header-css + (css [:.header {:margin-top "30px"}] + [:h1.project-name {:font-size "34px" + :display "inline"}] + [:h2.project-version {:font-size "18px" + :margin-top 0 + :display "inline" + :margin-left "10px"}] + [:.toc-link {:font-size "12px" + :margin-left "10px" + :color "#252519" + :text-decoration "none"}] + [:.toc-link:hover {:color "#5050A6"}] + [:.toc :h1 {:font-size "34px" + :margin 0}] + [:.docs-header {:border-bottom "dotted #aaa 1px" + :padding-bottom "10px" + :margin-bottom "25px"}] + [:.toc :h1 {:font-size "24px"}] + [:.toc {:border-bottom "solid #bbb 1px" + :margin-bottom "40px"}] + [:.toc :ul {:margin-left "20px" + :padding-left "0px" + :padding-top 0 + :margin-top 0}] + [:.toc :li {:list-style-type "none" + :padding-left 0}] + [:.dependencies {}] + [:.dependencies :table {:font-size "16px" + :width "99.99%" + :border "none" + :margin-left "20px"}] + [:.dependencies :td {:padding-right "20px;" + :white-space "nowrap"}] + [:.dependencies :.dotted {:width "99%"}] + [:.dependencies :.dotted :hr {:height 0 + :noshade "noshade" + :color "transparent" + :background-color "transparent" + :border-bottom "dotted #bbb 1px" + :border-top "none" + :border-left "none" + :border-right "none" + :margin-bottom "-6px"}] + [:.dependencies :.dep-version {:text-align "right"}] + [:.plugins :ul {:margin-left "20px" + :padding-left "0px" + :padding-top 0 + :margin-top 0}] + [:.plugins :li {:list-style-type "none" + :padding-left 0}] + [:.header :p {:margin-left "20px"}])) | ||||||||||||||||||
+ | (def floating-toc-css + (css [:#floating-toc {:position "fixed" + :top "10px" + :right "20px" + :height "20px" + :overflow "hidden" + :text-align "right"}] + [:#floating-toc :li {:list-style-type "none" + :margin 0 + :padding 0}])) | ||||||||||||||||||
+ | (def general-css + (css + [:body {:margin 0 + :padding 0 + :font-family "'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;" + :font-size "16px" + :color "#252519" + :background-color "#F5F5FF"}] + [:h1 {:font-size "20px" + :margin-top 0}] + [:h2 {:font-size "18px"}] + [:h3 {:font-size "16px"}] + [:a.anchor {:text-decoration "none" + :color "#252519"}] + [:a.anchor:hover {:color "#5050A6"}] + [:table {:border-spacing 0 + :border-bottom "solid #ddd 1px;" + :margin-bottom "10px"}] + [:code {:display "inline"}] + [:p {:margin-top "8px"}] + [:tr {:margin "0px" + :padding "0px"}] + [:td.docs {:width "410px" + :max-width "410px" + :vertical-align "top" + :margin "0px" + :padding-left "55px" + :padding-right "20px" + :border "none" + :background-color "#FFF"}] + [:td.docs :pre {:font-size "12px" + :overflow "hidden"}] + [:td.codes {:width "55%" + :background-color "#F5F5FF" + :vertical-align "top" + :margin "0px" + :padding-left "20px" + :border "none" + :overflow "hidden" + :font-size "10pt" + :border-left "solid #E5E5EE 1px"}] + [:td.spacer {:padding-bottom "40px"}] + [:pre :code {:display "block" + :padding "4px"}] + [:code {:background-color "ghostWhite" + :border "solid #DEDEDE 1px" + :padding-left "3px" + :padding-right "3px" + :font-size "14px"}] + [:.syntaxhighlighter :code {:font-size "13px"}] + [:.footer {:text-align "center"}])) | ||||||||||||||||||
Notice that we're inlining the css & javascript for SyntaxHighlighter ( | (defn page-template + [project-metadata opt-resources header toc content floating-toc] + (html + "<!DOCTYPE html>\n" + [:html + [:head + [:meta {:http-equiv "Content-Type" :content "text/html" :charset "utf-8"}] + [:meta {:name "description" :content (:description project-metadata)}] + (inline-css (str *resources* "shCore.css")) + (css + [:.syntaxhighlighter {:overflow "hidden !important"}]) + (inline-css (str *resources* "shThemeMarginalia.css")) + reset-css + header-css + floating-toc-css + general-css + (inline-js (str *resources* "jquery-1.7.1.min.js")) + (inline-js (str *resources* "xregexp-min.js")) + (inline-js (str *resources* "shCore.js")) + (inline-js (str *resources* "shBrushClojure.js")) + opt-resources + [:title (:name project-metadata) " -- Marginalia"]] + [:body + [:table + header + toc + content] + [:div {:class "footer"} + "Generated by " + [:a {:href "https://github.com/gdeer81/marginalia"} "Marginalia"] + ". " + "Syntax highlighting provided by Alex Gorbatchev's " + [:a {:href "http://alexgorbatchev.com/SyntaxHighlighter/"} + "SyntaxHighlighter"] + floating-toc] + (inline-js (str *resources* "app.js"))]])) | ||||||||||||||||||
Syntax highlighting is done a bit differently than docco. Instead of embedding +the highlighting metadata on the parse / html gen phase, we use SyntaxHighlighter +to do it in javascript. + | |||||||||||||||||||
This generates a stand alone html file (think | (defn uberdoc-html + [project-metadata docs] + (page-template + project-metadata + (opt-resources-html project-metadata) + (header-html project-metadata) + (toc-html {:uberdoc? true} docs) + (map #(groups-html {:uberdoc? true} %) docs) + (floating-toc-html docs))) | ||||||||||||||||||
+ | (defn index-html + [project-metadata docs] + (page-template + project-metadata + (opt-resources-html project-metadata) + (header-html project-metadata) + (toc-html {:uberdoc? false} docs) + ;; no contents)) ;; no floating toc | ||||||||||||||||||
no floating toc + | |||||||||||||||||||
+ | (defn single-page-html + [project-metadata doc all-docs] + (page-template + project-metadata + (opt-resources-html project-metadata) + ;; no header + ;; no toc + (groups-html {:uberdoc? false} doc) + ;; no floating toc)) | ||||||||||||||||||
A place to examine poor parser behavior. These should go in tests when they get written. + | (ns problem-cases.general) | ||||||||||||||||||
+ | [::foo] | ||||||||||||||||||
+ | {:foo 43} +{::foo 42} | ||||||||||||||||||
private docstring + | (defn ^:private private-fn []) | ||||||||||||||||||
docstring + | (defn public-fn [] + (let [x (private-fn)] + (count x))) | ||||||||||||||||||
Should have only this comment in the left margin. +See https://github.com/gdeer81/marginalia/issues/4 + | |||||||||||||||||||
+ | (defn parse-bool [v] (condp = (.trim (str v)) + "0" false + "1" true + "throw exception here")) | ||||||||||||||||||
Here is a docstring. It should be to the left. + | (defn a-function + [x] + (* x x)) | ||||||||||||||||||
Here is a docstring. It should be to the left. + | (defn b-function + [x] + "Here is just a string. It should be to the right." + (* x x)) | ||||||||||||||||||
Defines a relation... duh! + | (defprotocol Relation + (select [this predicate] + "Confines the query to rows for which the predicate is true + Ex. (select (table :users) (where (= :id 5)))") + (join [this table2 join_on] + "Joins two tables on join_on + Ex. (join (table :one) (table :two) :id) + (join (table :one) (table :two) + (where (= :one.col :two.col)))")) | ||||||||||||||||||
This is a defmulti docstring, it should also be on the left + | (defmulti bazfoo + class) | ||||||||||||||||||
+ | (defmethod bazfoo String [s] + "This is a defmethod docstring. It should be on the left." + (vec (seq s))) | ||||||||||||||||||
+ | (bazfoo "abc") | ||||||||||||||||||
This is a protocol docstring. It should be on the left. + | (defprotocol Foo + (lookup [cache e]) + (has? [cache e] ) + (hit [cache e]) + (miss [cache e ret])) | ||||||||||||||||||
This is also a docstring via metadata. It should be on the left. + | (def + a 42) | ||||||||||||||||||
This is also a docstring via metadata. It should be on the left. + | (def + b 42) | ||||||||||||||||||
This is also a docstring via metadata. It should be on the left. + | (def + c + "This is just a value. It should be on the right.") | ||||||||||||||||||
From fnparse + | |||||||||||||||||||
Padded on the front with optional whitespace. + | (comment + (do-template [rule-name token] + (h/defrule rule-name + (h/lit token)) + <escape-char-start> \\ + <str-delimiter> \" + <value-separator> \, + <name-separator> \: + <array-start> \[ + <array-end> \] + <object-start> \{ + <object-end> \})) | ||||||||||||||||||
Issue #26: Angle-bracket in Function Name Breaks Layout + | (defn <test [] nil) | ||||||||||||||||||
(defn test-html-entities-in-doc + [] + nil) | |||||||||||||||||||
+ | (defmulti kompile identity) | ||||||||||||||||||
+ | (defmethod kompile [:standard] + [_] + "GENERATED ALWAYS AS IDENTITY") | ||||||||||||||||||
Resulting expressions are wrapped in an anonymous function and, down the line,
+ | (defn strict-eval-op-fn + [op inc-ind-str ind-str op nl] + (ind-str + "(function() {" nl + (inc-ind-str + "var _out = arguments[0];" nl + "for(var _i=1; _i<arguments.length; _i++) {" nl + (inc-ind-str + "_out = _out " op " arguments[_i];") + nl + "}" nl + "return _out;") + nl + "})")) | ||||||||||||||||||
+ | '(defn special-forms [] + {'def handle-def + 'fn handle-fn + 'fn* handle-fn + 'set! handle-set + 'let handle-let + 'defn handle-defn + 'aget handle-aget + 'aset handle-aset + 'if handle-if + 'while handle-while + 'when handle-when + 'doto handle-doto + '-> handle--> + '->> handle-->> + 'not handle-not + 'do handle-do + 'cond handle-cond + '= (make-lazy-op '==) + '> (make-lazy-op '>) + '< (make-lazy-op '<) + '>= (make-lazy-op '>=) + '<= (make-lazy-op '<=) + 'or (make-lazy-op '||) + 'and (make-lazy-op '&&) + 'doseq handle-doseq + 'instanceof handle-instanceof + 'gensym handle-gensym + 'gensym-str handle-gensym-str}) | ||||||||||||||||||
+ | '(defn greater [a b] + (>= a b)) | ||||||||||||||||||
+ | '(fact + (greater 2 1) => truthy) | ||||||||||||||||||
+ | '(file->tickets commits) | ||||||||||||||||||
+ | (defmulti ns-kw-mm identity) +(defmethod ns-kw-mm ::foo [_] :problem-cases.general/foo) +(defmethod ns-kw-mm :user/foo [_] :user/foo) +(defmethod ns-kw-mm :foo [_] :foo) | ||||||||||||||||||