From a92b786ba66c4f0c5edcc0dd5fb57904c418c6e1 Mon Sep 17 00:00:00 2001
From: Chouser <chouser@n01se.net>
Date: Fri, 22 Oct 2010 07:57:10 -0400
Subject: [PATCH] Make doc support docstrings for special ops. CLJ-454

Also add special ops and namespaces to what find-doc searches through.
---
 src/clj/clojure/core.clj |  192 +++++++++++++++++++++++++++++++--------------
 1 files changed, 132 insertions(+), 60 deletions(-)

diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index b6fe498..f937743 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -3851,10 +3851,12 @@
       (reduce1 process-entry [] bents))))
 
 (defmacro let
-  "Evaluates the exprs in a lexical context in which the symbols in
+  "binding => binding-form init-expr
+
+  Evaluates the exprs in a lexical context in which the symbols in
   the binding-forms are bound to their respective init-exprs or parts
   therein."
-  {:added "1.0"}
+  {:added "1.0", :special-form true, :forms '[(let [bindings*] exprs*)]}
   [bindings & body]
   (assert-args let
      (vector? bindings) "a vector for its binding"
@@ -3881,16 +3883,14 @@
 
 ;redefine fn with destructuring and pre/post conditions
 (defmacro fn
-  "(fn name? [params* ] exprs*)
-  (fn name? ([params* ] exprs*)+)
-
-  params => positional-params* , or positional-params* & next-param
+  "params => positional-params* , or positional-params* & next-param
   positional-param => binding-form
   next-param => binding-form
   name => symbol
 
   Defines a function"
-  {:added "1.0"}
+  {:added "1.0", :special-form true,
+   :forms '[(fn name? [params* ] exprs*) (fn name? ([params* ] exprs*)+)]}
   [& sigs]
     (let [name (if (symbol? (first sigs)) (first sigs) nil)
           sigs (if name (next sigs) sigs)
@@ -3926,7 +3926,7 @@
   "Evaluates the exprs in a lexical context in which the symbols in
   the binding-forms are bound to their respective init-exprs or parts
   therein. Acts as a recur target."
-  {:added "1.0"}
+  {:added "1.0", :special-form true, :forms '[(loop [bindings*] exprs*)]}
   [bindings & body]
     (assert-args loop
       (vector? bindings) "a vector for its binding"
@@ -4230,75 +4230,146 @@
   [name & decls]
     (list* `defn (with-meta name (assoc (meta name) :private true)) decls))
 
-(defn print-doc [v]
-  (println "-------------------------")
-  (println (str (ns-name (:ns (meta v))) "/" (:name (meta v))))
-  (prn (:arglists (meta v)))
-  (when (:macro (meta v))
-    (println "Macro"))
-  (println " " (:doc (meta v))))
-
-(defn find-doc
-  "Prints documentation for any var whose documentation or name
- contains a match for re-string-or-pattern"
-  {:added "1.0"}
-  [re-string-or-pattern]
-    (let [re  (re-pattern re-string-or-pattern)]
-      (doseq [ns (all-ns)
-              v (sort-by (comp :name meta) (vals (ns-interns ns)))
-              :when (and (:doc (meta v))
-                         (or (re-find (re-matcher re (:doc (meta v))))
-                             (re-find (re-matcher re (str (:name (meta v)))))))]
-               (print-doc v))))
-
+(def ^:private special-doc-map
+  '{. {:url "java_interop#dot"
+       :forms [(.instanceMember instance args*)
+               (.instanceMember Classname args*)
+               (Classname/staticMethod args*)
+               Classname/staticField]
+       :doc "The instance member form works for both fields and methods.
+  They all expand into calls to the dot operator at macroexpansion time."}
+    def {:forms [(def symbol init?)]
+         :doc "Creates and interns a global var with the name
+  of symbol in the current namespace (*ns*) or locates such a var if
+  it already exists.  If init is supplied, it is evaluated, and the
+  root binding of the var is set to the resulting value.  If init is
+  not supplied, the root binding of the var is unaffected."}
+    do {:forms [(do exprs*)]
+        :doc "Evaluates the expressions in order and returns the value of
+  the last. If no expressions are supplied, returns nil."}
+    if {:forms [(if test then else?)]
+        :doc "Evaluates test. If not the singular values nil or false,
+  evaluates and yields then, otherwise, evaluates and yields else. If
+  else is not supplied it defaults to nil."}
+    monitor-enter {:forms [(monitor-enter x)]
+                   :doc "Synchronization primitive that should be avoided
+  in user code. Use the 'locking' macro."}
+    monitor-exit {:forms [(monitor-exit x)]
+                  :doc "Synchronization primitive that should be avoided
+  in user code. Use the 'locking' macro."}
+    new {:forms [(Classname. args*) (new Classname args*)]
+         :url "java_interop#new"
+         :doc "The args, if any, are evaluated from left to right, and
+  passed to the constructor of the class named by Classname. The
+  constructed object is returned."}
+    quote {:forms [(quote form)]
+           :doc "Yields the unevaluated form."}
+    recur {:forms [(recur exprs*)]
+           :doc "Evaluates the exprs in order, then, in parallel, rebinds
+  the bindings of the recursion point to the values of the exprs.
+  Execution then jumps back to the recursion point, a loop or fn method."}
+    set! {:forms[(set! var-symbol expr)
+                 (set! (. instance-expr instanceFieldName-symbol) expr)
+                 (set! (. Classname-symbol staticFieldName-symbol) expr)]
+          :url "vars#set"
+          :doc "Used to set thread-local-bound vars, Java object instance
+fields, and Java class static fields."}
+    throw {:forms [(throw expr)]
+           :doc "The expr is evaluated and thrown, therefore it should
+  yield an instance of some derivee of Throwable."}
+    try {:forms [(try expr* catch-clause* finally-clause?)]
+         :doc "catch-clause => (catch classname name expr*)
+  finally-clause => (finally expr*)
+
+  Catches and handles Java exceptions."}
+    var {:forms [(var symbol)]
+         :doc "The symbol must resolve to a var, and the Var object
+itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
+
+; only for backward-compatibility
 (defn special-form-anchor
   "Returns the anchor tag on http://clojure.org/special_forms for the
   special form x, or nil"
   {:added "1.0"
    :static true}
   [x]
-  (#{'. 'def 'do 'fn 'if 'let 'loop 'monitor-enter 'monitor-exit 'new
-  'quote 'recur 'set! 'throw 'try 'var} x))
+  ('#{. def do fn if let loop monitor-enter monitor-exit new
+      quote recur set! throw try var} x))
 
+(defn- special-doc [name-symbol]
+  (assoc (or (special-doc-map name-symbol) (meta (resolve name-symbol)))
+         :name name-symbol
+         :special-form true))
+
+; only for backward-compatibility
 (defn syntax-symbol-anchor
   "Returns the anchor tag on http://clojure.org/special_forms for the
   special form that uses syntax symbol x, or nil"
   {:added "1.0"
    :static true}
   [x]
-  ({'& 'fn 'catch 'try 'finally 'try} x))
+  ('{& fn catch try finally try} x))
+
+(defn- namespace-doc [nspace]
+  (assoc (meta nspace) :name (ns-name nspace)))
 
-(defn print-special-doc
-  [name type anchor]
+(defn- print-doc [m]
   (println "-------------------------")
-  (println name)
-  (println type)
-  (println (str "  Please see http://clojure.org/special_forms#" anchor)))
+  (println (str (when-let [ns (:ns m)] (str (ns-name ns) "/")) (:name m)))
+  (cond
+    (:forms m) (doseq [f (:forms m)]
+                 (print "  ")
+                 (prn f))
+    (:arglists m) (prn (:arglists m)))
+  (if (:special-form m)
+    (do
+      (println "Special Form")
+      (println " " (:doc m)) 
+      (if (contains? m :url)
+        (when (:url m)
+          (println (str "\n  Please see http://clojure.org/" (:url m))))
+        (println (str "\n  Please see http://clojure.org/special_forms#"
+                      (:name m)))))
+    (do
+      (when (:macro m)
+        (println "Macro")) 
+      (println " " (:doc m)))))
 
-(defn print-namespace-doc
-  "Print the documentation string of a Namespace."
+(defn find-doc
+  "Prints documentation for any var whose documentation or name
+ contains a match for re-string-or-pattern"
   {:added "1.0"}
-  [nspace]
-  (println "-------------------------")
-  (println (str (ns-name nspace)))
-  (println " " (:doc (meta nspace))))
+  [re-string-or-pattern]
+    (let [re (re-pattern re-string-or-pattern)
+          ms (concat (mapcat #(sort-by :name (map meta (vals (ns-interns %))))
+                             (all-ns))
+                     (map namespace-doc (all-ns))
+                     (map special-doc (keys special-doc-map)))]
+      (doseq [m ms
+              :when (and (:doc m)
+                         (or (re-find (re-matcher re (:doc m)))
+                             (re-find (re-matcher re (str (:name m))))))]
+               (print-doc m))))
 
 (defmacro doc
   "Prints documentation for a var or special form given its name"
   {:added "1.0"}
   [name]
-  (cond
-   (special-form-anchor `~name)
-   `(print-special-doc '~name "Special Form" (special-form-anchor '~name))
-   (syntax-symbol-anchor `~name)
-   `(print-special-doc '~name "Syntax Symbol" (syntax-symbol-anchor '~name))
-   :else
-    (let [nspace (find-ns name)]
-      (if nspace
-        `(print-namespace-doc ~nspace)
-        `(print-doc (var ~name))))))
-
- (defn tree-seq
+  (if-let [special-name ('{& fn catch try finally try} name)]
+    (#'print-doc (#'special-doc special-name))
+    (cond
+      (special-doc-map name) `(#'print-doc (#'special-doc '~name))
+      (resolve name) `(#'print-doc (meta (var ~name)))
+      (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))
+
+; only for backward-compatibility
+(defn print-namespace-doc
+  "Print the documentation string of a Namespace."
+  {:added "1.0"}
+  [nspace]
+  (print-doc (namespace-doc nspace)))
+
+(defn tree-seq
   "Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
    branch? must be a fn of one arg that returns true if passed a node
    that can have children (but may not).  children must be a fn of one
@@ -5698,12 +5769,13 @@
 
 
 (defmacro letfn 
-  "Takes a vector of function specs and a body, and generates a set of
-  bindings of functions to their names. All of the names are available
-  in all of the definitions of the functions, as well as the body.
+  "fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)
 
-  fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)"
-  {:added "1.0"}
+  Takes a vector of function specs and a body, and generates a set of
+  bindings of functions to their names. All of the names are available
+  in all of the definitions of the functions, as well as the body."
+  {:added "1.0", :forms '[(letfn [fnspecs*] exprs*)],
+   :special-form true, :url nil}
   [fnspecs & body] 
   `(letfn* ~(vec (interleave (map first fnspecs) 
                              (map #(cons `fn %) fnspecs)))
-- 
1.7.0.4

