diff --git a/codox/resources/codox/theme/default/css/default.css b/codox/resources/codox/theme/default/css/default.css index 33f78fed..e16171bc 100644 --- a/codox/resources/codox/theme/default/css/default.css +++ b/codox/resources/codox/theme/default/css/default.css @@ -44,7 +44,7 @@ h5.license { right: 0; height: 22px; color: #f5f5f5; - padding: 5px 7px; + padding: 7px 10px; } #content { @@ -97,6 +97,8 @@ h5.license { } #header h1 { + display: block; + float: left; margin: 0; padding: 0; font-size: 18px; @@ -121,6 +123,26 @@ h5.license { color: #f5f5f5; } +#langs { + display: block; + float: left; + font-size: 16px; + margin: 0 10px; +} + +#langs .lang { + display: inline-block; + margin: 0 2px; + padding: 0 7px; + background-color: #717171; + border-radius: 6px; + vertical-align: middle; +} + +#langs .lang.current { + background-color: #a33; +} + .sidebar a { color: #333; } @@ -325,7 +347,8 @@ h5.license { h4.type, h4.dynamic, h4.added, -h4.deprecated { +h4.deprecated, +h4.lang { float: left; margin: 3px 10px 15px 0; font-size: 15px; @@ -336,7 +359,8 @@ h4.deprecated { .public h4.type, .public h4.dynamic, .public h4.added, -.public h4.deprecated { +.public h4.deprecated, +.public h4.lang { font-size: 13px; font-weight: bold; margin: 3px 0 0 10px; @@ -344,7 +368,8 @@ h4.deprecated { .members h4.type, .members h4.added, -.members h4.deprecated { +.members h4.deprecated, +.members h4.lang { margin-top: 1px; } @@ -364,6 +389,16 @@ h4.deprecated { color: #880000; } +.public h4.lang, .public h4.lang a { + font-weight: normal; + font-variant: normal; + color: #717171; +} + +.public h4.lang.current { + color: #a33; +} + .namespace { margin-bottom: 30px; } diff --git a/codox/src/codox/main.clj b/codox/src/codox/main.clj index 1a11ab30..4327a0b4 100644 --- a/codox/src/codox/main.clj +++ b/codox/src/codox/main.clj @@ -78,13 +78,52 @@ namespaces)) (defn- read-namespaces + "Returns { } for cross-platform opts, + or otherwise." [{:keys [language root-path source-paths namespaces metadata exclude-vars] :as opts}] - (let [reader (namespace-readers language)] - (-> (reader source-paths (select-keys opts [:exception-handler])) - (filter-namespaces namespaces) - (remove-excluded-vars exclude-vars) - (add-source-paths root-path source-paths) - (add-ns-defaults metadata)))) + (if (:cross-platform? opts) + (reduce + (fn [m language] + (assoc m language + (read-namespaces + (assoc opts + :language language + :cross-platform? false)))) + {} + (:languages opts)) + (let [reader (namespace-readers language)] + (-> (reader source-paths (select-keys opts [:exception-handler])) + (filter-namespaces namespaces) + (remove-excluded-vars exclude-vars) + (add-source-paths root-path source-paths) + (add-ns-defaults metadata))))) + +(defn- get-var-langs + "Returns { { }} for given namespaces." + ([language namespaces var-langs] + (reduce + (fn [var-langs ns] + (reduce + (fn [var-langs public-var] + (update-in var-langs [(:name ns) (:name public-var)] + #(conj (or % #{}) language))) + var-langs + (:publics ns))) + var-langs + namespaces)) + + ([options namespaces] + (if-not (:cross-platform? options) + (get-var-langs (:language options) namespaces {}) + (reduce + (fn [var-langs language] + (get-var-langs language (get namespaces language) var-langs)) + {} + (:languages options))))) + +(comment (get-var-langs {:languages #{:clojure :clojurescript}} + '({:name codox.main :publics ({:name defaults} {:name bar})} + {:name codox.foo :publics ({:name bar})}))) (defn- read-documents [{:keys [doc-paths doc-files] :or {doc-files :all}}] (cond @@ -113,15 +152,29 @@ :themes [:default] :git-commit (delay (git-commit root-path))})) +(defn- cross-platform-options [{:keys [language] :as opts}] + (if-not (set? language) + opts ; {:language } + (if (= (count language) 1) + (assoc opts :language (first language)) ; {:language } + + ;; Cross-platform case: {:language nil, :languages } + (assoc opts + :language nil + :languages language + :cross-platform? true)))) + (defn generate-docs "Generate documentation from source files." ([] (generate-docs {})) ([options] - (let [options (merge defaults options) + (let [options (-> (merge defaults options) cross-platform-options) write-fn (writer options) namespaces (read-namespaces options) - documents (read-documents options)] + documents (read-documents options) + var-langs (get-var-langs options namespaces)] (write-fn (assoc options :namespaces namespaces - :documents documents))))) + :documents documents + :var-langs var-langs))))) diff --git a/codox/src/codox/writer/html.clj b/codox/src/codox/writer/html.clj index 0a4f9c81..102e218a 100644 --- a/codox/src/codox/writer/html.clj +++ b/codox/src/codox/writer/html.clj @@ -146,8 +146,36 @@ (if-let [doc (:doc metadata)] (markdown-to-html doc project ns))]) +(defn- language-info + ([language] + (when language + (case language + :clojure {:sort 1, :ext "clj", :filename-suffix ".clj", :name "Clojure"} + :clojurescript {:sort 2, :ext "cljs", :filename-suffix ".cljs", :name "ClojureScript"} + (ex-info (str "Unexpected language: `" language "`") + {:language language})))) + + ([language base-language] + (when-let [info (language-info language)] + (if (= language base-language) + (assoc info :filename-suffix "") + info)))) + +(defn- sorted-languages [languages] + (sort-by #(:sort (language-info %)) languages)) + +(comment (sorted-languages #{:clojurescript :clojure})) + +(defn- index-filename [language] + (str "index" (:filename-suffix (language-info language)) ".html")) + (defn- ns-filename [namespace] - (str (:name namespace) ".html")) + (str (:name namespace) + (:filename-suffix (language-info (:language namespace) (:base-language namespace))) + ".html")) + +(comment + (ns-filename {:name 'my-ns :language :clojure})) (defn- ns-filepath [output-dir namespace] (str output-dir "/" (ns-filename namespace))) @@ -235,11 +263,12 @@ [:span.bottom]]))) (defn- index-link [project on-index?] - (list - [:h3.no-link [:span.inner "Project"]] - [:ul.index-link - [:li.depth-1 {:class (if on-index? "current")} - (link-to "index.html" [:div.inner "Index"])]])) + (when-not (:cross-platform? project) + (list + [:h3.no-link [:span.inner "Project"]] + [:ul.index-link + [:li.depth-1 {:class (if on-index? "current")} + (link-to "index.html" [:div.inner "Index"])]]))) (defn- topics-menu [project current-doc] (if-let [docs (seq (:documents project))] @@ -275,16 +304,28 @@ (get-in project [:html :namespace-list] default))) (defn- namespaces-menu [project current-ns] - (let [namespaces (:namespaces project)] + (when (:show-namespaces? project) + (let [namespaces (:namespaces project)] + (list + [:h3.no-link [:span.inner "Namespaces"]] + (case (namespace-list-type project) + :flat (flat-namespaces namespaces current-ns) + :nested (nested-namespaces namespaces current-ns)))))) + +(defn- platforms-menu [project] + (when (:show-platforms? project) (list - [:h3.no-link [:span.inner "Namespaces"]] - (case (namespace-list-type project) - :flat (flat-namespaces namespaces current-ns) - :nested (nested-namespaces namespaces current-ns))))) + [:h3.no-link [:span.inner "Platforms"]] + [:ul.index-link + (for [language (sorted-languages (:languages project))] + [:li.depth-1 + (link-to (index-filename language) + [:div.inner (:name (language-info language))])])]))) (defn- primary-sidebar [project & [current]] [:div.sidebar.primary (index-link project (nil? current)) + (platforms-menu project) (topics-menu project current) (namespaces-menu project current)]) @@ -314,10 +355,20 @@ [:span.project-name (h (:name project))] " " [:span.project-version (h (:version project))]]) +(defn- header-platforms [project] + (when (:cross-platform? project) + (let [{:keys [language languages]} project] + [:div#langs + (for [language* (sorted-languages languages)] + (if (= language language*) + [:div.lang.current (:ext (language-info language*))] + [:div.lang (link-to (index-filename language*) (:ext (language-info language*)))]))]))) + (defn- header [project] [:div#header [:h2 "Generated by " (link-to "https://github.com/weavejester/codox" "Codox")] - [:h1 (link-to "index.html" (project-title project))]]) + [:h1 (link-to "index.html" (project-title project))] + (header-platforms project)]) (defn- package [project] (if-let [p (:package project)] @@ -360,16 +411,28 @@ [:ul.topics (for [doc docs] [:li (link-to (doc-filename doc) (h (:title doc)))])])) - [:h2 "Namespaces"] - (for [namespace (sort-by :name (:namespaces project))] - [:div.namespace - [:h3 (link-to (ns-filename namespace) (h (:name namespace)))] - [:div.doc (format-docstring project nil (update-in namespace [:doc] util/summary))] - [:div.index - [:p "Public variables and functions:"] - (unordered-list - (for [var (sorted-public-vars namespace)] - (list " " (link-to (var-uri namespace var) (h (:name var))) " ")))]])]])) + + (when (:show-platforms? project) + (list + [:h2 "Platforms"] + [:p "This project includes code for multiple platforms, please " + [:strong "choose a platform"] " to view its documentation:"] + [:ul + (for [language (sorted-languages (:languages project))] + [:li (link-to (index-filename language) (:name (language-info language)))])])) + + (when (:show-namespaces? project) + (list + [:h2 "Namespaces"] + (for [namespace (sort-by :name (:namespaces project))] + [:div.namespace + [:h3 (link-to (ns-filename namespace) (h (:name namespace)))] + [:div.doc (format-docstring project nil (update-in namespace [:doc] util/summary))] + [:div.index + [:p "Public variables and functions:"] + (unordered-list + (for [var (sorted-public-vars namespace)] + (list " " (link-to (var-uri namespace var) (h (:name var))) " ")))]])))]])) (defmulti format-document "Format a document into HTML." @@ -425,6 +488,18 @@ [:h4.type (name (:type var))]) (if (:dynamic var) [:h4.dynamic "dynamic"]) + + (if (:cross-platform? project) + (let [var-langs (get-in project [:var-langs (:name namespace) (:name var)]) + {:keys [language languages]} project] + + (for [language* (sorted-languages languages)] + (when (contains? var-langs language*) + (if (= language language*) + [:h4.lang.current (:ext (language-info language*))] + [:h4.lang (link-to (var-uri (assoc namespace :language language*) var) + (:ext (language-info language*)))]))))) + (added-and-deprecated-docs var) (if (:type-sig var) [:div.type-sig @@ -464,13 +539,60 @@ (doseq [dir dirs] (.mkdirs (io/file output-dir dir)))) +(defn- cross-platform-namespaces + [namespaces language base-language] + (map + #(assoc % + :language language + :base-language base-language) + (get namespaces language))) + (defn- write-index [output-dir project] - (spit (io/file output-dir "index.html") (transform-html project (index-page project)))) + (let [{:keys [namespaces cross-platform?]} project] + + (when cross-platform? + ;; Write an index file for each language + (doseq [language (:languages project)] + (let [namespaces (cross-platform-namespaces namespaces language (:base-language project)) + project + (assoc project + :namespaces namespaces + :language language + :show-platforms? false + :show-namespaces? true)] + (spit (io/file output-dir (index-filename language)) + (transform-html project (index-page project)))))) + + ;; Always write a main index file + (let [project (assoc project + :show-platforms? cross-platform? + :show-namespaces? (not cross-platform?))] + (spit (io/file output-dir (index-filename nil)) + (transform-html project (index-page project)))))) (defn- write-namespaces [output-dir project] - (doseq [namespace (:namespaces project)] - (spit (ns-filepath output-dir namespace) - (transform-html project (namespace-page project namespace))))) + (let [{:keys [namespaces cross-platform?]} project] + + (if cross-platform? + ;; Write namespace files for each language + (doseq [language (:languages project)] + (let [namespaces (cross-platform-namespaces namespaces language (:base-language project))] + (doseq [namespace namespaces] + (let [project (assoc project + :namespaces namespaces + :language language + :show-platforms? false + :show-namespaces? true)] + (spit (ns-filepath output-dir namespace) + (transform-html project (namespace-page project namespace))))))) + + ;; Write namespace files for only language + (doseq [namespace namespaces] + (let [project (assoc project + :show-platforms? false + :show-namespaces? true)] + (spit (ns-filepath output-dir namespace) + (transform-html project (namespace-page project namespace)))))))) (defn- write-documents [output-dir project] (doseq [document (:documents project)]