Comments (8)
Yes I understand your frustration. Unfortunately what you're trying to do isn't really possible, and unlikely to be something I'm going to try to incorporate in future. You always have to strike a balance between simplicity of the API, and supporting all the permutations of formatting that people may want in future.
Here's a similar issue: #17
As you've found out, kludging it is tricky too...
Call
just separates the items with commas. Here's the config for Call. You've also looked at List
, but that doesn't help - it just does the same without the parenthesis.
Here's a kludge that works... Defs
is meant for grouping definitions: const
, var
etc. It separates the items with \n
and wraps the list in parenthesis. Here's the config. You can also use Op
to get a comma. If you're more interested in the readability of your generated source than the source of your generator, then this is a way to do it:
func ExampleKludge() {
c := jen.Id("foo").Defs(
jen.Id("a").Op(",").Comment("foo"),
jen.Id("b").Op(",").Comment("foo"),
jen.Id("c").Op(",").Comment("foo"),
)
fmt.Printf("%#v\n", c)
// Output:
// foo(
// a, // foo
// b, // foo
// c, // foo
// )
}
p.s. In your examples you didn't mention it, so I'm not sure if you're forgetting the most simple usage of Call
:
c := Id("foo").Call(
Id("a"),
Id("b"),
Id("c"),
)
fmt.Printf("%#v", c)
// Output:
// foo(a, b, c)
from jennifer.
There's two things I'm going to investigate in future as a result of this issue:
-
Right now, adding a Comment("...") in the middle of a statement is usually a bad idea. Jen blindly adds the remaining tokens after rendering the
// ...
comment. Perhaps comments can be buffered, and only rendered at the end of the statement, perhaps forcing a\n
to be added. I can see lots of potential edge cases, so this is a long-shot. -
Right now it's not possible to define your own types that satisfy the Code interface, because of the private methods. If the methods were public, it may be possible to create your own
Code
types that could be added to statements withAdd
. This would allow unlimited flexibility while keeping code more readable. You could create aCallWithLinefeedsAndComments
type. I will investigate the feasibility of this and the potential pitfalls.
from jennifer.
@dave Thanks for the detailed explanation and reference to the other issue. Sorry I didn't find that one & save you some trouble, but I appreciate you explaining things and providing the (admittedly bit kludgey) workaround.
I definitely understand the difficulty of designing an API that is flexible enough to support what people want while maintaining simplicity/ease of use. It's a tough balance and I applaud your focus on developer ergonomics here; given your experience with code generation and the Go grammar, you have a much better understanding of the potential pitfalls involved than I do. 😄
from jennifer.
Hey @jawnsy I just added a feature which might partially solve your problem. Check out the new Custom
and CustomFunc
methods... https://godoc.org/github.com/dave/jennifer/jen#example-Custom
from jennifer.
@dave Great, thanks! This works perfectly for my use case, and produces more prettier code than doing it by hand (because I'm usually inconsistent with multi-line/single-line invocations)
jen
is now spitting out some code like:
row := txn.QueryRowContext(
ctx,
"SELECT account_id, attributes, name, display_name, description, archived FROM accounts WHERE account_id = $1",
accountID,
)
return row.Scan(
&account.Metadata.ID,
&account.Metadata.Attributes,
&account.Name,
&account.DisplayName,
&account.Description,
&account.Archived,
)
It would be nice if all the existing types were exposed as Option
objects, with methods to customize specific fields. For example, there could be:
var CallOptions = jen.Options{
Close: ")",
Multi: false,
Open: "(",
Separator: ",",
}
and then users could do something like: jen.CallOptions.WithMulti(true)
(using the same options for Call, make it a multi-line invocation) or even something like jen.CallOptions.WithOpen("{").WithClose("}")
(though of course that would produce invalid syntax)
Also, since dep
prefers tagged releases, any ideas when you'll push the next release tag?
Cheers,
Jonathan
from jennifer.
Yeah I'll just do a little bit more testing before tagging this.
from jennifer.
The new CustomFunc
that you've introduced is very nice.
It's proving handy for some unit tests, where I have a function that accepts a Group
and I want to assert that the function generates what's expected. Configuring the Options
to produce minimal clutter makes it relatively easy to assert that what's generated is what's expected. For example there are no \t
s from formatting to deal with.
I can have a function like this.
func generateAndAssert(t *testing.T, generate func(*jen.Group), expectedLines ...string) {
options := jen.Options{
Open: "",
Close: "",
Separator: "",
Multi: true,
}
jen.CustomFunc(options, func(group *jen.Group) {
generate(group)
generated := group.GoString()
expected := strings.Join(expectedLines, "\n")
assert.Equal(t, "\n"+expected, generated)
})
}
And I can then call it from a unit test.
func SomeTest(t *testing.T) {
generateAndAssert(t, func(g *jen.Group) {
functionToBeTestedThatAddsStatementsToTheGroup(g)
}, "the first expected generated line", "the second line")
}
from jennifer.
@pekim glad it's proving useful! @jawnsy I've released this as v0.19.0
, so feel free to pin that. I'll close this now.
from jennifer.
Related Issues (20)
- Better ways to manage import names and aliases HOT 5
- Get *jen.Statement from reflect.Type HOT 1
- Plain struct field tag HOT 1
- Create groups in my code? HOT 19
- [go1.19, go1.20] Indented Multiline Struct Comments have tab characters HOT 3
- example with multiple return types HOT 5
- Support `Array` for `Value` besides `Dict`? HOT 2
- Dict sorts keys by string, breaking existing/natural ordering HOT 2
- Please read: Issues for improving the readability or formatting of outputted code.
- Creating a function receiver HOT 2
- Looking for call chaining including line-feeds HOT 7
- How to generate struct parameter in function qual HOT 2
- Callback with errors?
- Support multiline string literal (backtick) HOT 2
- Feature: ability to provide custom imports sorter function
- Feature Request: Do not render [] if .Types() is called with no arguments HOT 1
- Non-int untyped numeric literal HOT 2
- How to generate complex type field definitions in Struct HOT 3
- Custom types on interface HOT 3
- How to Gennerate var()? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jennifer.