diff src/clojure/contrib/pprint/dispatch.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/contrib/pprint/dispatch.clj	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,447 @@
     1.4 +;; dispatch.clj -- part of the pretty printer for Clojure
     1.5 +
     1.6 +;; by Tom Faulhaber
     1.7 +;; April 3, 2009
     1.8 +
     1.9 +;   Copyright (c) Tom Faulhaber, Feb 2009. All rights reserved.
    1.10 +;   The use and distribution terms for this software are covered by the
    1.11 +;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
    1.12 +;   which can be found in the file epl-v10.html at the root of this distribution.
    1.13 +;   By using this software in any fashion, you are agreeing to be bound by
    1.14 +;   the terms of this license.
    1.15 +;   You must not remove this notice, or any other, from this software.
    1.16 +
    1.17 +;; This module implements the default dispatch tables for pretty printing code and
    1.18 +;; data.
    1.19 +
    1.20 +(in-ns 'clojure.contrib.pprint)
    1.21 +
    1.22 +(defn use-method
    1.23 +  "Installs a function as a new method of multimethod associated with dispatch-value. "
    1.24 +  [multifn dispatch-val func]
    1.25 +  (. multifn addMethod dispatch-val func))
    1.26 +
    1.27 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.28 +;; Implementations of specific dispatch table entries
    1.29 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.30 +
    1.31 +;;; Handle forms that can be "back-translated" to reader macros
    1.32 +;;; Not all reader macros can be dealt with this way or at all. 
    1.33 +;;; Macros that we can't deal with at all are:
    1.34 +;;; ;  - The comment character is aborbed by the reader and never is part of the form
    1.35 +;;; `  - Is fully processed at read time into a lisp expression (which will contain concats
    1.36 +;;;      and regular quotes).
    1.37 +;;; ~@ - Also fully eaten by the processing of ` and can't be used outside.
    1.38 +;;; ,  - is whitespace and is lost (like all other whitespace). Formats can generate commas
    1.39 +;;;      where they deem them useful to help readability.
    1.40 +;;; ^ - Adding metadata completely disappears at read time and the data appears to be
    1.41 +;;;      completely lost.
    1.42 +;;;
    1.43 +;;; Most other syntax stuff is dealt with directly by the formats (like (), [], {}, and #{})
    1.44 +;;; or directly by printing the objects using Clojure's built-in print functions (like
    1.45 +;;; :keyword, \char, or ""). The notable exception is #() which is special-cased.
    1.46 +
    1.47 +(def reader-macros
    1.48 +     {'quote "'", 'clojure.core/deref "@", 
    1.49 +      'var "#'", 'clojure.core/unquote "~"})
    1.50 +
    1.51 +(defn pprint-reader-macro [alis]
    1.52 +  (let [^String macro-char (reader-macros (first alis))]
    1.53 +    (when (and macro-char (= 2 (count alis)))
    1.54 +      (.write ^java.io.Writer *out* macro-char)
    1.55 +      (write-out (second alis))
    1.56 +      true)))
    1.57 +
    1.58 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.59 +;; Dispatch for the basic data types when interpreted
    1.60 +;; as data (as opposed to code).
    1.61 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.62 +
    1.63 +;;; TODO: inline these formatter statements into funcs so that we
    1.64 +;;; are a little easier on the stack. (Or, do "real" compilation, a
    1.65 +;;; la Common Lisp)
    1.66 +
    1.67 +;;; (def pprint-simple-list (formatter-out "~:<~@{~w~^ ~_~}~:>"))
    1.68 +(defn pprint-simple-list [alis]
    1.69 +  (pprint-logical-block :prefix "(" :suffix ")"
    1.70 +    (loop [alis (seq alis)]
    1.71 +      (when alis
    1.72 +	(write-out (first alis))
    1.73 +	(when (next alis)
    1.74 +	  (.write ^java.io.Writer *out* " ")
    1.75 +	  (pprint-newline :linear)
    1.76 +	  (recur (next alis)))))))
    1.77 +
    1.78 +(defn pprint-list [alis]
    1.79 +  (if-not (pprint-reader-macro alis)
    1.80 +    (pprint-simple-list alis)))
    1.81 +
    1.82 +;;; (def pprint-vector (formatter-out "~<[~;~@{~w~^ ~_~}~;]~:>"))
    1.83 +(defn pprint-vector [avec]
    1.84 +  (pprint-logical-block :prefix "[" :suffix "]"
    1.85 +    (loop [aseq (seq avec)]
    1.86 +      (when aseq
    1.87 +	(write-out (first aseq))
    1.88 +	(when (next aseq)
    1.89 +	  (.write ^java.io.Writer *out* " ")
    1.90 +	  (pprint-newline :linear)
    1.91 +	  (recur (next aseq)))))))
    1.92 +
    1.93 +(def pprint-array (formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>"))
    1.94 +
    1.95 +;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>"))
    1.96 +(defn pprint-map [amap]
    1.97 +  (pprint-logical-block :prefix "{" :suffix "}"
    1.98 +    (loop [aseq (seq amap)]
    1.99 +      (when aseq
   1.100 +	(pprint-logical-block 
   1.101 +          (write-out (ffirst aseq))
   1.102 +          (.write ^java.io.Writer *out* " ")
   1.103 +          (pprint-newline :linear)
   1.104 +          (write-out (fnext (first aseq))))
   1.105 +        (when (next aseq)
   1.106 +          (.write ^java.io.Writer *out* ", ")
   1.107 +          (pprint-newline :linear)
   1.108 +          (recur (next aseq)))))))
   1.109 +
   1.110 +(def pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>"))
   1.111 +(defn pprint-ref [ref]
   1.112 +  (pprint-logical-block  :prefix "#<Ref " :suffix ">"
   1.113 +    (write-out @ref)))
   1.114 +(defn pprint-atom [ref]
   1.115 +  (pprint-logical-block :prefix "#<Atom " :suffix ">"
   1.116 +    (write-out @ref)))
   1.117 +(defn pprint-agent [ref]
   1.118 +  (pprint-logical-block :prefix "#<Agent " :suffix ">"
   1.119 +    (write-out @ref)))
   1.120 +
   1.121 +(defn pprint-simple-default [obj]
   1.122 +  (cond 
   1.123 +    (.isArray (class obj)) (pprint-array obj)
   1.124 +    (and *print-suppress-namespaces* (symbol? obj)) (print (name obj))
   1.125 +    :else (pr obj)))
   1.126 +
   1.127 +
   1.128 +(defmulti 
   1.129 +  *simple-dispatch*
   1.130 +  "The pretty print dispatch function for simple data structure format."
   1.131 +  {:arglists '[[object]]} 
   1.132 +  class)
   1.133 +
   1.134 +(use-method *simple-dispatch* clojure.lang.ISeq pprint-list)
   1.135 +(use-method *simple-dispatch* clojure.lang.IPersistentVector pprint-vector)
   1.136 +(use-method *simple-dispatch* clojure.lang.IPersistentMap pprint-map)
   1.137 +(use-method *simple-dispatch* clojure.lang.IPersistentSet pprint-set)
   1.138 +(use-method *simple-dispatch* clojure.lang.Ref pprint-ref)
   1.139 +(use-method *simple-dispatch* clojure.lang.Atom pprint-atom)
   1.140 +(use-method *simple-dispatch* clojure.lang.Agent pprint-agent)
   1.141 +(use-method *simple-dispatch* nil pr)
   1.142 +(use-method *simple-dispatch* :default pprint-simple-default)
   1.143 +
   1.144 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.145 +;;; Dispatch for the code table
   1.146 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.147 +
   1.148 +(declare pprint-simple-code-list)
   1.149 +
   1.150 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.151 +;;; Format something that looks like a simple def (sans metadata, since the reader
   1.152 +;;; won't give it to us now).
   1.153 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.154 +
   1.155 +(def pprint-hold-first (formatter-out "~:<~w~^ ~@_~w~^ ~_~@{~w~^ ~_~}~:>"))
   1.156 +
   1.157 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.158 +;;; Format something that looks like a defn or defmacro
   1.159 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.160 +
   1.161 +;;; Format the params and body of a defn with a single arity
   1.162 +(defn- single-defn [alis has-doc-str?]
   1.163 +  (if (seq alis)
   1.164 +    (do
   1.165 +      (if has-doc-str?
   1.166 +        ((formatter-out " ~_"))
   1.167 +        ((formatter-out " ~@_")))
   1.168 +      ((formatter-out "~{~w~^ ~_~}") alis))))
   1.169 +
   1.170 +;;; Format the param and body sublists of a defn with multiple arities
   1.171 +(defn- multi-defn [alis has-doc-str?]
   1.172 +  (if (seq alis)
   1.173 +    ((formatter-out " ~_~{~w~^ ~_~}") alis)))
   1.174 +
   1.175 +;;; TODO: figure out how to support capturing metadata in defns (we might need a 
   1.176 +;;; special reader)
   1.177 +(defn pprint-defn [alis]
   1.178 +  (if (next alis) 
   1.179 +    (let [[defn-sym defn-name & stuff] alis
   1.180 +          [doc-str stuff] (if (string? (first stuff))
   1.181 +                            [(first stuff) (next stuff)]
   1.182 +                            [nil stuff])
   1.183 +          [attr-map stuff] (if (map? (first stuff))
   1.184 +                             [(first stuff) (next stuff)]
   1.185 +                             [nil stuff])]
   1.186 +      (pprint-logical-block :prefix "(" :suffix ")"
   1.187 +        ((formatter-out "~w ~1I~@_~w") defn-sym defn-name)
   1.188 +        (if doc-str
   1.189 +          ((formatter-out " ~_~w") doc-str))
   1.190 +        (if attr-map
   1.191 +          ((formatter-out " ~_~w") attr-map))
   1.192 +        ;; Note: the multi-defn case will work OK for malformed defns too
   1.193 +        (cond
   1.194 +         (vector? (first stuff)) (single-defn stuff (or doc-str attr-map))
   1.195 +         :else (multi-defn stuff (or doc-str attr-map)))))
   1.196 +    (pprint-simple-code-list alis)))
   1.197 +
   1.198 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.199 +;;; Format something with a binding form
   1.200 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.201 +
   1.202 +(defn pprint-binding-form [binding-vec]
   1.203 +  (pprint-logical-block :prefix "[" :suffix "]"
   1.204 +    (loop [binding binding-vec]
   1.205 +      (when (seq binding)
   1.206 +        (pprint-logical-block binding
   1.207 +          (write-out (first binding))
   1.208 +          (when (next binding)
   1.209 +            (.write ^java.io.Writer *out* " ")
   1.210 +            (pprint-newline :miser)
   1.211 +            (write-out (second binding))))
   1.212 +        (when (next (rest binding))
   1.213 +          (.write ^java.io.Writer *out* " ")
   1.214 +          (pprint-newline :linear)
   1.215 +          (recur (next (rest binding))))))))
   1.216 +
   1.217 +(defn pprint-let [alis]
   1.218 +  (let [base-sym (first alis)]
   1.219 +    (pprint-logical-block :prefix "(" :suffix ")"
   1.220 +      (if (and (next alis) (vector? (second alis)))
   1.221 +        (do
   1.222 +          ((formatter-out "~w ~1I~@_") base-sym)
   1.223 +          (pprint-binding-form (second alis))
   1.224 +          ((formatter-out " ~_~{~w~^ ~_~}") (next (rest alis))))
   1.225 +        (pprint-simple-code-list alis)))))
   1.226 +
   1.227 +
   1.228 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.229 +;;; Format something that looks like "if"
   1.230 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.231 +
   1.232 +(def pprint-if (formatter-out "~:<~1I~w~^ ~@_~w~@{ ~_~w~}~:>"))
   1.233 +
   1.234 +(defn pprint-cond [alis]
   1.235 +  (pprint-logical-block :prefix "(" :suffix ")"
   1.236 +    (pprint-indent :block 1)
   1.237 +    (write-out (first alis))
   1.238 +    (when (next alis)
   1.239 +      (.write ^java.io.Writer *out* " ")
   1.240 +      (pprint-newline :linear)
   1.241 +     (loop [alis (next alis)]
   1.242 +       (when alis
   1.243 +         (pprint-logical-block alis
   1.244 +          (write-out (first alis))
   1.245 +          (when (next alis)
   1.246 +            (.write ^java.io.Writer *out* " ")
   1.247 +            (pprint-newline :miser)
   1.248 +            (write-out (second alis))))
   1.249 +         (when (next (rest alis))
   1.250 +           (.write ^java.io.Writer *out* " ")
   1.251 +           (pprint-newline :linear)
   1.252 +           (recur (next (rest alis)))))))))
   1.253 +
   1.254 +(defn pprint-condp [alis]
   1.255 +  (if (> (count alis) 3) 
   1.256 +    (pprint-logical-block :prefix "(" :suffix ")"
   1.257 +      (pprint-indent :block 1)
   1.258 +      (apply (formatter-out "~w ~@_~w ~@_~w ~_") alis)
   1.259 +      (loop [alis (seq (drop 3 alis))]
   1.260 +        (when alis
   1.261 +          (pprint-logical-block alis
   1.262 +            (write-out (first alis))
   1.263 +            (when (next alis)
   1.264 +              (.write ^java.io.Writer *out* " ")
   1.265 +              (pprint-newline :miser)
   1.266 +              (write-out (second alis))))
   1.267 +          (when (next (rest alis))
   1.268 +            (.write ^java.io.Writer *out* " ")
   1.269 +            (pprint-newline :linear)
   1.270 +            (recur (next (rest alis)))))))
   1.271 +    (pprint-simple-code-list alis)))
   1.272 +
   1.273 +;;; The map of symbols that are defined in an enclosing #() anonymous function
   1.274 +(def *symbol-map* {})
   1.275 +
   1.276 +(defn pprint-anon-func [alis]
   1.277 +  (let [args (second alis)
   1.278 +        nlis (first (rest (rest alis)))]
   1.279 +    (if (vector? args)
   1.280 +      (binding [*symbol-map* (if (= 1 (count args)) 
   1.281 +                               {(first args) "%"}
   1.282 +                               (into {} 
   1.283 +                                     (map 
   1.284 +                                      #(vector %1 (str \% %2)) 
   1.285 +                                      args 
   1.286 +                                      (range 1 (inc (count args))))))]
   1.287 +        ((formatter-out "~<#(~;~@{~w~^ ~_~}~;)~:>") nlis))
   1.288 +      (pprint-simple-code-list alis))))
   1.289 +
   1.290 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.291 +;;; The master definitions for formatting lists in code (that is, (fn args...) or
   1.292 +;;; special forms).
   1.293 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.294 +
   1.295 +;;; This is the equivalent of (formatter-out "~:<~1I~@{~w~^ ~_~}~:>"), but is
   1.296 +;;; easier on the stack.
   1.297 +
   1.298 +(defn pprint-simple-code-list [alis]
   1.299 +  (pprint-logical-block :prefix "(" :suffix ")"
   1.300 +    (pprint-indent :block 1)
   1.301 +    (loop [alis (seq alis)]
   1.302 +      (when alis
   1.303 +	(write-out (first alis))
   1.304 +	(when (next alis)
   1.305 +	  (.write ^java.io.Writer *out* " ")
   1.306 +	  (pprint-newline :linear)
   1.307 +	  (recur (next alis)))))))
   1.308 +
   1.309 +;;; Take a map with symbols as keys and add versions with no namespace.
   1.310 +;;; That is, if ns/sym->val is in the map, add sym->val to the result.
   1.311 +(defn two-forms [amap]
   1.312 +  (into {} 
   1.313 +        (mapcat 
   1.314 +         identity 
   1.315 +         (for [x amap] 
   1.316 +           [x [(symbol (name (first x))) (second x)]]))))
   1.317 +
   1.318 +(defn add-core-ns [amap]
   1.319 +  (let [core "clojure.core"]
   1.320 +    (into {}
   1.321 +          (map #(let [[s f] %] 
   1.322 +                  (if (not (or (namespace s) (special-symbol? s)))
   1.323 +                    [(symbol core (name s)) f]
   1.324 +                    %))
   1.325 +               amap))))
   1.326 +
   1.327 +(def *code-table*
   1.328 +     (two-forms
   1.329 +      (add-core-ns
   1.330 +       {'def pprint-hold-first, 'defonce pprint-hold-first, 
   1.331 +	'defn pprint-defn, 'defn- pprint-defn, 'defmacro pprint-defn, 'fn pprint-defn,
   1.332 +        'let pprint-let, 'loop pprint-let, 'binding pprint-let,
   1.333 +        'with-local-vars pprint-let, 'with-open pprint-let, 'when-let pprint-let,
   1.334 +	'if-let pprint-let, 'doseq pprint-let, 'dotimes pprint-let,
   1.335 +	'when-first pprint-let,
   1.336 +        'if pprint-if, 'if-not pprint-if, 'when pprint-if, 'when-not pprint-if,
   1.337 +        'cond pprint-cond, 'condp pprint-condp,
   1.338 +        'fn* pprint-anon-func,
   1.339 +        '. pprint-hold-first, '.. pprint-hold-first, '-> pprint-hold-first,
   1.340 +        'locking pprint-hold-first, 'struct pprint-hold-first,
   1.341 +        'struct-map pprint-hold-first, 
   1.342 +        })))
   1.343 +
   1.344 +(defn pprint-code-list [alis]
   1.345 +  (if-not (pprint-reader-macro alis) 
   1.346 +    (if-let [special-form (*code-table* (first alis))]
   1.347 +      (special-form alis)
   1.348 +      (pprint-simple-code-list alis))))
   1.349 +
   1.350 +(defn pprint-code-symbol [sym] 
   1.351 +  (if-let [arg-num (sym *symbol-map*)]
   1.352 +    (print arg-num)
   1.353 +    (if *print-suppress-namespaces* 
   1.354 +      (print (name sym))
   1.355 +      (pr sym))))
   1.356 +
   1.357 +(defmulti 
   1.358 +  *code-dispatch*
   1.359 +  "The pretty print dispatch function for pretty printing Clojure code."
   1.360 +  {:arglists '[[object]]} 
   1.361 +  class)
   1.362 +
   1.363 +(use-method *code-dispatch* clojure.lang.ISeq pprint-code-list)
   1.364 +(use-method *code-dispatch* clojure.lang.Symbol pprint-code-symbol)
   1.365 +
   1.366 +;; The following are all exact copies of *simple-dispatch*
   1.367 +(use-method *code-dispatch* clojure.lang.IPersistentVector pprint-vector)
   1.368 +(use-method *code-dispatch* clojure.lang.IPersistentMap pprint-map)
   1.369 +(use-method *code-dispatch* clojure.lang.IPersistentSet pprint-set)
   1.370 +(use-method *code-dispatch* clojure.lang.Ref pprint-ref)
   1.371 +(use-method *code-dispatch* clojure.lang.Atom pprint-atom)
   1.372 +(use-method *code-dispatch* clojure.lang.Agent pprint-agent)
   1.373 +(use-method *code-dispatch* nil pr)
   1.374 +(use-method *code-dispatch* :default pprint-simple-default)
   1.375 +
   1.376 +(set-pprint-dispatch *simple-dispatch*)
   1.377 +
   1.378 +
   1.379 +;;; For testing
   1.380 +(comment
   1.381 +
   1.382 +(with-pprint-dispatch *code-dispatch* 
   1.383 +  (pprint 
   1.384 +   '(defn cl-format 
   1.385 +      "An implementation of a Common Lisp compatible format function"
   1.386 +      [stream format-in & args]
   1.387 +      (let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
   1.388 +            navigator (init-navigator args)]
   1.389 +        (execute-format stream compiled-format navigator)))))
   1.390 +
   1.391 +(with-pprint-dispatch *code-dispatch* 
   1.392 +  (pprint 
   1.393 +   '(defn cl-format 
   1.394 +      [stream format-in & args]
   1.395 +      (let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
   1.396 +            navigator (init-navigator args)]
   1.397 +        (execute-format stream compiled-format navigator)))))
   1.398 +
   1.399 +(with-pprint-dispatch *code-dispatch* 
   1.400 +  (pprint
   1.401 +   '(defn- -write 
   1.402 +      ([this x]
   1.403 +         (condp = (class x)
   1.404 +           String 
   1.405 +           (let [s0 (write-initial-lines this x)
   1.406 +                 s (.replaceFirst s0 "\\s+$" "")
   1.407 +                 white-space (.substring s0 (count s))
   1.408 +                 mode (getf :mode)]
   1.409 +             (if (= mode :writing)
   1.410 +               (dosync
   1.411 +                (write-white-space this)
   1.412 +                (.col_write this s)
   1.413 +                (setf :trailing-white-space white-space))
   1.414 +               (add-to-buffer this (make-buffer-blob s white-space))))
   1.415 +
   1.416 +           Integer
   1.417 +           (let [c ^Character x]
   1.418 +             (if (= (getf :mode) :writing)
   1.419 +               (do 
   1.420 +                 (write-white-space this)
   1.421 +                 (.col_write this x))
   1.422 +               (if (= c (int \newline))
   1.423 +                 (write-initial-lines this "\n")
   1.424 +                 (add-to-buffer this (make-buffer-blob (str (char c)) nil))))))))))
   1.425 +
   1.426 +(with-pprint-dispatch *code-dispatch* 
   1.427 +  (pprint 
   1.428 +   '(defn pprint-defn [writer alis]
   1.429 +      (if (next alis) 
   1.430 +        (let [[defn-sym defn-name & stuff] alis
   1.431 +              [doc-str stuff] (if (string? (first stuff))
   1.432 +                                [(first stuff) (next stuff)]
   1.433 +                                [nil stuff])
   1.434 +              [attr-map stuff] (if (map? (first stuff))
   1.435 +                                 [(first stuff) (next stuff)]
   1.436 +                                 [nil stuff])]
   1.437 +          (pprint-logical-block writer :prefix "(" :suffix ")"
   1.438 +                                (cl-format true "~w ~1I~@_~w" defn-sym defn-name)
   1.439 +                                (if doc-str
   1.440 +                                  (cl-format true " ~_~w" doc-str))
   1.441 +                                (if attr-map
   1.442 +                                  (cl-format true " ~_~w" attr-map))
   1.443 +                                ;; Note: the multi-defn case will work OK for malformed defns too
   1.444 +                                (cond
   1.445 +                                  (vector? (first stuff)) (single-defn stuff (or doc-str attr-map))
   1.446 +                                  :else (multi-defn stuff (or doc-str attr-map)))))
   1.447 +        (pprint-simple-code-list writer alis)))))
   1.448 +)
   1.449 +nil
   1.450 +