Hi everyone,


Over at CIDER we're adding a feature where the author of a macro (or 
function) can specify how that macro should be indented by adding an :indent 
metadata to its definition. This way the editor (and other tools, like 
cljfmt) will know what's the proper way of indenting any macro (even those 
custom-defined) without having to hardcode a bajillion names.

Here's an example of how you specify the indent spec for your macros


(defmacro with-out-str
  "[DOCSTRING]"
  {:indent 0}
  [& body]
  ...cut for brevity...)

(defmacro defrecord
  "[DOCSTRING]"
  {:indent [2 nil nil [1]]}
  [name fields & opts+specs]
  ...cut for brevity)

(defmacro with-in-str
  "[DOCSTRING]"
  {:indent 1}
  [s & body]
  ...cut for brevity...)


We'd like to hear any opinions on the practicality of this (specially from 
authors of other editors). 
Below, I'll be saying “macros” all the time, but this applies just the same 
to functions.

*Special arguments* 


Many macros have a number of “special” arguments, followed by an arbitrary 
number of “non-special” arguments (sometimes called the body). The 
“non-special” arguments have a small indentation (usually 2 spaces). These 
special arguments are usually on the same line as the macro name, but, when 
necessary, they are placed on a separate line with additional indentation.

For instance, defrecord has two special arguments, and here's how it might 
be indented:


(defrecord TheNameOfTheRecord
    [a pretty long argument list]
  SomeType
  (assoc [_ x]
    (.assoc pretty x 10)))


Here's another way one could do it:


(defrecord TheNameOfTheRecord
           [a pretty long argument list]
  SomeType
  (assoc [_ x]
    (.assoc pretty x 10)))


*The point of the indent spec is not to specify how many spaces to use.* 


The point is just to say “a defrecord has *2* special arguments”, and then 
let the editor and the user come to an agreement on how many spaces they 
like to use for special and non-special arguments.

*Internal indentation* 


The issue goes a bit deeper. Note the last argument in that defrecord. A 
regular function call would be internally indented as 

(assoc [_ x]
       (.assoc pretty x 10))

But this is not a regular function call, it's a definition. So we want to 
specify this form internally has 1 special argument (the arglist vector), 
so that it will be indented like this:

(assoc [_ x]
  (.assoc pretty x 10))

The indent spec we're working on does this as well. It lets you specify 
that, for each argument beyond the 2nd, if it is a form, it should be 
internally indented as if it had 1 special argument.

*The spec* 


An indent spec can be:

   - nil (or absent), meaning *“indent like a regular function call”*. 
   - A vector (or list) meaning that this function/macro takes a number of 
   special arguments, and then all other arguments are non-special. 
      - The first element of this vector is an integer indicating how many 
      special arguments this function/macro takes. 
      - Each following element is an indent spec on its own, and it applies 
      to the argument on the same position as this element. So, when that 
      argument is a form, this element specifies how to indent that form 
      internally (if it's not a form the spec is irrelevant). 
      - If the function/macro has more aguments than the vector has 
      elements, the last element of the vector applies to all remaining 
arguments. 
   - If the whole spec is just an integer n, that is shorthand for [n]. 


*Examples* 


So, for instance, if I specify the defrecord spec as [2 nil nil [1]], this 
is saying:

   - defrecord has 2 special arguments 
   - The first two arguments don't get special internal indentation 
   - All remaining arguments have an internal indent spec of [1] (which 
   means only the arglist is indented specially). 

Another example, reify is [1 nil [1]] (which should be easy to see now). 


(reify Object 
  (toString [this]
    (something)
    else
    "here"))


For something more complicated: letfn is [1 [[1]] nil]. This means

   - letfn has one special argument (the bindings list). 
   - The first arg has an indent spec of [[1]], which means all forms 
   *inside* the first arg have an indent spec of [1]. 
   - The second argument, and all other arguments, are regular forms. 

(letfn [(twice [x]
          (* x 2))
        (six-times [y]
          (* (twice y) 3))]
  (println "Twice 15 =" (twice 15))
  (println "Six times 15 =" 
           (six-times 15)))

-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to