|
1 | | -(ns java-javadocs.core |
| 1 | +(ns clojure.java.doc.impl |
2 | 2 | (:require |
3 | 3 | [clojure.string :as str]) |
4 | 4 | (:import [com.vladsch.flexmark.html2md.converter FlexmarkHtmlConverter] |
5 | 5 | [org.jsoup Jsoup])) |
6 | 6 |
|
7 | 7 | (set! *warn-on-reflection* true) |
8 | 8 |
|
9 | | -(defn javadoc-url [^String classname] |
| 9 | +(defn- check-java-version [^String version-str] |
| 10 | + (let [version (Integer/parseInt version-str) |
| 11 | + min-version 17] |
| 12 | + (when (< version min-version) |
| 13 | + (throw (ex-info |
| 14 | + (str "Java " min-version " or higher is required. Current version: " version-str) |
| 15 | + {:current-version version-str |
| 16 | + :minimum-version min-version}))))) |
| 17 | + |
| 18 | +(defn- javadoc-url [^String classname] |
10 | 19 | (let [java-version (System/getProperty "java.specification.version") |
| 20 | + _ (check-java-version java-version) |
11 | 21 | classname (str/replace classname #"\$.*" "") |
12 | 22 | klass (Class/forName classname) |
13 | 23 | module-name (.getName (.getModule klass)) |
14 | 24 | url-path (.replace classname \. \/)] |
15 | 25 | (str "https://docs.oracle.com/en/java/javase/" java-version "/docs/api/" module-name "/" url-path ".html"))) |
16 | 26 |
|
17 | | -(defn html-to-md [^String html] |
| 27 | +(defn- html-to-md [^String html] |
18 | 28 | (.convert ^FlexmarkHtmlConverter (.build (FlexmarkHtmlConverter/builder)) html)) |
19 | 29 |
|
20 | 30 | (defn- resolve-class-name [class-part] |
|
105 | 115 | (str "^[" (str/join " " clojure-types) "] " class-part separator method-name)) |
106 | 116 | (str class-part separator method-name)))) |
107 | 117 |
|
108 | | - (defn parse-javadoc |
109 | | - "parse the javadoc HTML for a class or method into a data structure: |
110 | | - {:classname 'java.lang.String' |
111 | | - :class-description-html '...' |
112 | | - :class-description-md '...' |
113 | | - :methods [...] |
114 | | - :selected-method [{:signature 'valueOf(char[] data)' |
115 | | - :description 'Returns the string representation...' |
116 | | - :static? true |
117 | | - :clojure-call '^[char/1] String/valueOf' |
118 | | - :method-description-html '...' |
119 | | - :method-description-md '...'}]}" |
120 | | - [s param-tags] |
121 | | - (let [[class-part method-part] (str/split s #"/\.?" 2) |
122 | | - class-name (resolve-class-name class-part) |
123 | | - doc (Jsoup/parse (slurp (javadoc-url class-name))) |
124 | | - class-desc-section (.selectFirst doc "section.class-description") |
125 | | - method-rows (.select doc "div.method-summary-table.col-second") |
126 | | - all-methods (vec (for [^org.jsoup.nodes.Element method-div method-rows] |
127 | | - (let [desc-div ^org.jsoup.nodes.Element (.nextElementSibling method-div) |
128 | | - signature (.text (.select method-div "code")) |
129 | | - modifier-div ^org.jsoup.nodes.Element (.previousElementSibling method-div) |
130 | | - modifier-html (when modifier-div (.html modifier-div)) |
131 | | - is-static? (and modifier-html (str/includes? modifier-html "static"))] |
132 | | - {:signature signature |
133 | | - :description (.text (.select desc-div ".block")) |
134 | | - :static? is-static? |
135 | | - :clojure-call (clojure-call-syntax class-part signature is-static?)}))) |
136 | | - class-html (.outerHtml class-desc-section) |
137 | | - result {:classname class-name |
138 | | - :class-description-html class-html |
139 | | - :class-description-md (html-to-md class-html) |
140 | | - :methods all-methods}] |
141 | | - (if method-part |
142 | | - (let [filtered (filter-methods all-methods method-part param-tags)] |
143 | | - (assoc result :selected-method |
144 | | - (mapv #(get-method-detail doc %) filtered))) |
145 | | - result))) |
146 | | - |
147 | | -(defn javadoc-data-fn [s param-tags] |
148 | | - (parse-javadoc s param-tags)) |
149 | | - |
150 | | -(defn- print-javadoc [{:keys [class-description-md selected-method]}] |
| 118 | +(defn parse-javadoc |
| 119 | + "parse the javadoc HTML for a class or method into a data structure: |
| 120 | + {:classname 'java.lang.String' |
| 121 | + :class-description-html '...' |
| 122 | + :class-description-md '...' |
| 123 | + :methods [...] |
| 124 | + :selected-method [{:signature 'valueOf(char[] data)' |
| 125 | + :description 'Returns the string representation...' |
| 126 | + :static? true |
| 127 | + :clojure-call '^[char/1] String/valueOf' |
| 128 | + :method-description-html '...' |
| 129 | + :method-description-md '...'}]}" |
| 130 | + [s param-tags] |
| 131 | + (let [[class-part method-part] (str/split s #"/\.?" 2) |
| 132 | + class-name (resolve-class-name class-part) |
| 133 | + doc (Jsoup/parse (slurp (javadoc-url class-name))) |
| 134 | + class-desc-section (.selectFirst doc "section.class-description") |
| 135 | + method-rows (.select doc "div.method-summary-table.col-second") |
| 136 | + all-methods (vec (for [^org.jsoup.nodes.Element method-div method-rows] |
| 137 | + (let [desc-div ^org.jsoup.nodes.Element (.nextElementSibling method-div) |
| 138 | + signature (.text (.select method-div "code")) |
| 139 | + modifier-div ^org.jsoup.nodes.Element (.previousElementSibling method-div) |
| 140 | + modifier-html (when modifier-div (.html modifier-div)) |
| 141 | + is-static? (and modifier-html (str/includes? modifier-html "static"))] |
| 142 | + {:signature signature |
| 143 | + :description (.text (.select desc-div ".block")) |
| 144 | + :static? is-static? |
| 145 | + :clojure-call (clojure-call-syntax class-part signature is-static?)}))) |
| 146 | + class-html (.outerHtml class-desc-section) |
| 147 | + result {:classname class-name |
| 148 | + :class-description-html class-html |
| 149 | + :class-description-md (html-to-md class-html) |
| 150 | + :methods all-methods}] |
| 151 | + (if method-part |
| 152 | + (let [filtered (filter-methods all-methods method-part param-tags)] |
| 153 | + (assoc result :selected-method |
| 154 | + (mapv #(get-method-detail doc %) filtered))) |
| 155 | + result))) |
| 156 | + |
| 157 | +(defn print-javadoc [{:keys [class-description-md selected-method]}] |
151 | 158 | (if selected-method |
152 | 159 | (doseq [{:keys [method-description-md]} selected-method] |
153 | 160 | (println method-description-md)) |
154 | 161 | (println class-description-md))) |
155 | | - |
156 | | -(defn javadoc-fn [s param-tags] |
157 | | - (print-javadoc (javadoc-data-fn s param-tags))) |
158 | | - |
159 | | -(defmacro javadoc-data |
160 | | - "a map containg javadoc data for a class or method |
161 | | - Examples: |
162 | | - (javadoc-data String) ; Get class data |
163 | | - (javadoc-data String/valueOf) ; Get data for all valueOf overloads |
164 | | - (javadoc-data ^[char/1] String/valueOf) ; Get data for specific overload" |
165 | | - [class-or-method] |
166 | | - `(javadoc-data-fn ~(str class-or-method) '~(:param-tags (meta class-or-method)))) |
167 | | - |
168 | | -(defmacro javadoc |
169 | | - "print javadoc for a class or method |
170 | | - Examples: |
171 | | - (javadoc String) ; Print class description |
172 | | - (javadoc String/valueOf) ; Print all valueOf overloads |
173 | | - (javadoc ^[char/1] String/valueOf) ; Print specific overload" |
174 | | - [class-or-method] |
175 | | - `(javadoc-fn ~(str class-or-method) '~(:param-tags (meta class-or-method)))) |
0 commit comments