kkinnear / zprint

Executables, uberjar, and library to beautifully format Clojure and Clojurescript source code and s-expressions.
MIT License
547 stars 46 forks source link

Function to format a whole top-level file that takes code instead of a string? #319

Closed didibus closed 1 month ago

didibus commented 1 month ago

Sometimes I've got top-level forms as code inside a list, and I would like to print them into a formatted string, but there does not seem to be a function in zprint that does that which accepts a list of top-level code forms.

kkinnear commented 1 month ago

Thanks for asking. That said, I'm not sure what you are asking, I'm sorry to say. If you could give me an example of your input and describe fairly explicitly what you want as output, I expect I can figure out how to get you what you want. I just don't know, yet, what that is.

didibus commented 1 month ago

I have:

'((defn foo [a b] (+ a b)) (defn bar [x y] (- x y)))

And would like:

"(defn foo
   [a b]
   (+ a b))

 (defn bar
   [x y]
   (- x y))"
kkinnear commented 1 month ago

I'm sorry that I still don't think I'm understanding what you want. Let me ask you some questions that will help me respond in a meaningful way:

  1. Are you using zprint as: o A library to process Clojure structures in memory o A program to process files containing Clojure source, using either the pre-built binaries or the uberjar o A library to process files containing Clojure where you read in the Clojure and present it to the library. o Something else...

If you are using zprint as a library:

  1. Is your input to zprint actually a quoted list of defn forms?

If you are using zprint to process files, using either the pre-built binaries or the uberjar:

  1. What is actually in the file that you are feeding to zprint? Is it a quoted list of defn forms?

Given what you have written in the previous comment, if I assume that you using zprint as a library to process a list of defn forms, then I can see how to get the output you want as follows:

; What you gave in the previous comment
zprint.core=> i319
"'((defn foo [a b] (+ a b)) (defn bar [x y] (- x y)))\n"

; The results of (def i319e (eval (read-string i319)))
zprint.core=> i319e
((defn foo [a b] (+ a b)) (defn bar [x y] (- x y)))

; It is `:width 20` in order to make the `defn` forms format on multiple lines, otherwise they will format on a single line
; since they are short.  If the whole problem is actually getting things that are short to format in multiple lines, please make
; that clear since that is possible but not where I think you are going.  I expect you made the `defn` forms short just for
; this example
zprint.core=> (interpose "\n\n" (map #(zprint-str % {:width 20}) i319e))
("(defn foo\n  [a b]\n  (+ a b))" "\n\n" "(defn bar\n  [x y]\n  (- x y))")

: The results
zprint.core=> (print *1)
((defn foo
  [a b]
  (+ a b))

 (defn bar
  [x y]
  (- x y)))

I very much doubt this is actually how you want to use zprint, so I'm hoping you will answer the questions and explain a bit more about the environment in which you are using zprint so I can better assist you in getting useful results.

Thanks!

didibus commented 1 month ago

No worries.

I'm using it as: o A library to process Clojure structures in memory

Is your input to zprint actually a quoted list of defn forms?

Yes, not just defn, but a quoted list of top level forms, can include def and ns forms as well for example.

I've tried something similar to what you suggest, and it does work, but it processes each form on its own, meaning I have to manually put line breaks between forms for example. I'd like zprint to treat the list of forms as if it were a file and format it all together including rules about spacing between top-level forms.

What I'm doing is I read a Clojure source file using the reader, then I edit it as Clojure data, and I want to spit it back out, but I want to do so well formated.

kkinnear commented 1 month ago

Thanks for the additional information.

Interestingly, zprint doesn't actually have rules about spacing between top level forms. In contrast to zprint's behavior when formatting source within an expression (where to a first approximation, zprint ignores all white space), zprint preserves all top level blank lines (and comments, of course). You can cause zprint to put a specific amount of blank lines between top level expressions (again, when processing source files) by using :parse {:interpose <string>}} where <string> consists of a series of newlines. Again, this is all for processing source files.

Given what you want to do, having zprint process each expression in isolation and placing a fixed number of newlines between them with the interpose function seems like a reasonable approach to solving your problem. If I were to implement something directly in zprint to give you what you want, it would look a lot like this:

(interpose "\n\n" (map #(zprint-str % {}) <list ot top-level forms>))

where the first argument to interpose is the space you want between top level forms, and the empty map is where you put the zprint options you want to be used for this run. And the <list-of-top-level-forms> is ... the list of top level forms.

It isn't clear to me that if I were to add such a capability directly to zprint, it would produce much ... well, any added value beyond you executing that expression on your own.

That said, I'm always interested in meeting people's needs (as you can see by the plethora of configuaration options in zprint), so if there is something that you think would make a real difference if it were implemented directly in zprint, please let me know.

didibus commented 1 month ago

That works then. I thought there were more formatting that took into account the top level forms within themselves. But if not, adding the spacing between them myself is good enough.