The following, which I relinquish into the public domain and certify is
original to me, takes a string and reformats it as Clojure code, returning a
string. It behaves similarly to the Enclojure reformatter, but:
1. Outside string literals and comments, it will take care of all
   spacing.
2. Comments get one space before and one after the ; before the
   comment text. If the comment text originally started with a space
   none is inserted after the ;.
3. If two comments are on successive lines and the second can be
   aligned with the first by inserting additional spaces before the
   ;, the comments are aligned in this manner; exception to item 2.
4. Delimiters in character literals (e.g. \{) don't screw up
   the indenting of susbequent lines.

Note that this doesn't use the reader, which at first blush would seem to
make things simpler. But using the reader would clobber comments, expand #()
and the like, and fail if the input contained unbalanced delimiters. The
below code appears to indent the same as Enclojure in the face of unbalanced
delimiters.

As a little demonstration, the code below appears as formatted by itself:

(defn format-code [string]
  (loop [s string col 0 dstack [] out [] space nil incl false
         insl false incm false lwcr false sups true cmindent nil]
    (if-let [c (first s)] ; test comment 1 { ; )
      (let [r (rest s)    ; test comment 2 ( [
            begins "([{"
            ends ")]}" ; test comment 3 " ;
            delim-indents (zipmap begins [1 0 0]) ; test comment 4
            delim-ends (zipmap ends begins)
            sups-char? #{\' \` \~ \@ \#} ; characters to suppress
            indent (fn []                ; spaces after
                     (if (or (empty? r) (empty? dstack))
                       [\newline]
                       (into [\newline]
                         (repeat
                           (let [[delim pos] (peek dstack)]
                             (+ pos (delim-indents delim)))
                           \space))))
            conc (fn [c]
                   (if sups [c] (conj space c)))
            pop-d (fn [stack c]
                    (if (empty? stack)
                      []
                      (let [ps (pop stack)]
                        (if (empty? ps)
                          []
                          (if (= (first (peek stack)) (delim-ends c))
                            ps
                            (recur ps c))))))]
        (cond
          incm (let [nl (or (= c \newline) (= c \return))
                     spc (Character/isWhitespace c)
                     cc (if nl
                          (indent)
                          (if spc
                            (if-not sups [c])
                            [c]))]
                 (recur
                   r (if nl (dec (count cc)) (+ col (count cc)))
                   dstack (into out cc) nil false false
                   (if-not nl incm) (= c \return) nl cmindent))
          (= c \") (let [cc (if (or incl insl) [c] [\space c])]
                     (recur
                       r (+ col (count cc)) dstack (into out cc)
                       (if (and insl (not incl)) [\space]) false
                       (if incl insl (not insl)) false false false
                       cmindent))
          (or incl insl) (recur
                           r (if (or (= c \newline) (= c \return))
                               0
                               (inc col))
                           dstack (conj out c) nil
                           (and insl (not incl) (= c \\)) insl false
                           false false cmindent)
          (= c \;) (let [padding (if sups 0 1)
                         padding (if cmindent
                                   (max padding (- cmindent col))
                                   padding)
                         padding (repeat padding \space)
                         padding (concat padding [\; \space])]
                     (recur
                       r (+ col (count padding)) dstack
                       (into out padding) nil false false true false
                       true (+ col (count padding) -2)))
          (= c \\) (let [cc (if sups [c] [\space c])]
                     (recur
                       r (+ col (count cc)) dstack (into out cc) nil
                       true false false false false cmindent))
          (or
            (and (= c \newline) (not lwcr))
            (= c \return)) (let [i (indent)]
                             (recur
                               r (dec (count i)) dstack (into out i)
                               nil false false false (= c \return)
                               true nil))
          (= c \newline) (recur
                           r col dstack out nil false false false
                           false true cmindent)
          (Character/isWhitespace c) (recur
                                       r col dstack out [\space]
                                       false false false false sups
                                       cmindent)
          (delim-indents c) (let [cc (if sups [c] [\space c])
                                  cn (count cc)]
                              (recur
                                r (+ col cn)
                                (conj dstack [c (+ col cn)])
                                (into out cc) nil false false false
                                false true cmindent))
          (delim-ends c) (recur
                           r (inc col) (pop-d dstack c) (conj out c)
                           [\space] false false false false false
                           cmindent)
          :else (let [cc (conc c)]
                  (recur
                    r (+ col (count cc)) dstack (into out cc) nil
                    false false false false (sups-char? c)
                    cmindent))))
      (apply str out))))

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to