diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1b62d09..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go -go: - - 1.x -notificaitons: - email: - recipients: dave@brophy.uk - on_failure: always -install: - - go get -u github.com/dave/courtney - - go get -t -v ./... -script: - - courtney -e -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index bc88595..7dea473 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ -[![Go Reference](https://pkg.go.dev/badge/github.com/dave/jennifer/jen.svg)](https://pkg.go.dev/github.com/dave/jennifer/jen) -[![Build Status](https://travis-ci.org/dave/jennifer.svg?branch=master)](https://travis-ci.org/dave/jennifer) [![Go Report Card](https://goreportcard.com/badge/github.com/dave/jennifer)](https://goreportcard.com/report/github.com/dave/jennifer) -[![codecov](https://img.shields.io/badge/codecov-100%25-brightgreen.svg)](https://codecov.io/gh/dave/jennifer) +[![docs](https://pkg.go.dev/badge/github.com/dave/jennifer/jen.svg)](https://pkg.go.dev/github.com/dave/jennifer/jen) ![stability-stable](https://img.shields.io/badge/stability-stable-brightgreen.svg) -[![Sourcegraph](https://sourcegraph.com/github.com/dave/jennifer/-/badge.svg)](https://sourcegraph.com/github.com/dave/jennifer?badge) # Jennifer Jennifer is a code generator for Go. @@ -40,9 +37,9 @@ func main() { go get -u github.com/dave/jennifer/jen ``` -### Need help? -If you get stuck, have a question, would like a code review, or just want a -chat: I'm happy to help! Feel free to open an issue, email me or mention @dave +### Need help? +If you get stuck, have a question, would like a code review, or just want a +chat: I'm happy to help! Feel free to open an issue, email me or mention @dave in your PR. ### Examples @@ -53,7 +50,7 @@ Jennifer has a comprehensive suite of examples - see [godoc](https://godoc.org/g * [go-contentful-generator](https://github.com/nicolai86/go-contentful-generator) ### Rendering -For testing, a File or Statement can be rendered with the fmt package +For testing, a File or Statement can be rendered with the fmt package using the %#v verb. ```go @@ -63,12 +60,12 @@ fmt.Printf("%#v", c) // a("b") ``` -This is not recommended for use in production because any error will cause a -panic. For production use, [File.Render](#render) or [File.Save](#save) are +This is not recommended for use in production because any error will cause a +panic. For production use, [File.Render](#render) or [File.Save](#save) are preferred. # Identifiers -**Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +**Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Id Id renders an identifier. @@ -85,7 +82,7 @@ fmt.Printf("%#v", c) ``` ### Dot -Dot renders a period followed by an identifier. Use for fields and selectors. +Dot renders a period followed by an identifier. Use for fields and selectors. ```go c := Qual("a.b/c", "Foo").Call().Dot("Bar").Index(Lit(0)).Dot("Baz") @@ -149,9 +146,9 @@ fmt.Printf("%#v", c) ``` # Keywords -[Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) -Simple keywords, predeclared identifiers and built-in functions are self +Simple keywords, predeclared identifiers and built-in functions are self explanatory: | Construct | Name | @@ -174,7 +171,7 @@ fmt.Printf("%#v", c) Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [Switch, Case](#switch-select), [Return](#return) and [Map](#map) are explained below. # Operators -[Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) Op renders the provided operator / token. @@ -208,9 +205,9 @@ fmt.Printf("%#v", c) ``` # Braces -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) -Several methods render curly braces, summarized below: +Several methods render curly braces, summarized below: | Name | Prefix | Separator | Example | | ------------------------------ | ------------ | --------- | -------------------------------------| @@ -251,7 +248,7 @@ fmt.Printf("%#v", c) A special case applies when used directly after Case or Default, where the braces are omitted. This allows use in switch and select statements. [See example](#switch-select). ### Interface, Struct -Interface and Struct render the keyword followed by a statement list enclosed +Interface and Struct render the keyword followed by a statement list enclosed by curly braces. ```go @@ -293,7 +290,7 @@ fmt.Printf("%#v", c) ``` # Parentheses -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) Several methods output parenthesis, summarized below: @@ -382,7 +379,7 @@ fmt.Printf("%#v", c) ``` # Control flow -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### If, For If and For render the keyword followed by a semicolon separated list. @@ -459,7 +456,7 @@ fmt.Printf("%#v", c) ``` # Collections -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Map Map renders the keyword followed by a single item enclosed by square brackets. Use for map definitions. @@ -552,7 +549,7 @@ fmt.Printf("%#v", c) Note: the items are ordered by key when rendered to ensure repeatable code. # Literals -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Lit Lit renders a literal. Lit supports only built-in types (bool, string, int, complex128, float64, @@ -583,7 +580,7 @@ fmt.Printf("%#v", c) // a := 2 ``` -For the default constant types (bool, int, float64, string, complex128), Lit +For the default constant types (bool, int, float64, string, complex128), Lit will render the untyped constant. | Code | Output | @@ -594,7 +591,7 @@ will render the untyped constant. | `Lit("foo")` | `"foo"` | | `Lit(0 + 1i)` | `(0 + 1i)` | -For all other built-in types (float32, int8, int16, int32, int64, uint, uint8, +For all other built-in types (float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, complex64), Lit will also render the type. | Code | Output | @@ -604,7 +601,7 @@ uint16, uint32, uint64, uintptr, complex64), Lit will also render the type. | `Lit(uint8(0x1))` | `uint8(0x1)` | | `Lit(complex64(0 + 1i))` | `complex64(0 + 1i)` | -The built-in alias types byte and rune need a special case. LitRune and LitByte +The built-in alias types byte and rune need a special case. LitRune and LitByte render rune and byte literals. | Code | Output | @@ -613,7 +610,7 @@ render rune and byte literals. | `LitByte(byte(0x1))` | `byte(0x1)` | # Comments -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Comment Comment adds a comment. If the provided string contains a newline, the @@ -668,11 +665,67 @@ fmt.Printf("%#v", c) // foo := "bar" // foo is the string "bar" ``` +# Generics +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Generics** [Helpers](#helpers) [Misc](#misc) [File](#file) + +It is hoped that with the introduction of generics with Go 1.18, the need to generate code +will be reduced. However, for the sake of completeness, we now support generics including +the `any` and `comparable` predeclared identifiers, and the `Types` and `Union` lists. To +emit the approximation (`~`) token, use `Op("~")`. + +### Types + +Types renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. + +### Union + +Union renders a pipe separated list. Use for union type constraints. + +### Examples + +```go +c := Func().Id("Keys").Types( + Id("K").Comparable(), + Id("V").Any(), +).Params( + Id("m").Map(Id("K")).Id("V"), +).Index().Id("K").Block() +fmt.Printf("%#v", c) +// Output: +// func Keys[K comparable, V any](m map[K]V) []K {} +``` +```go +c := Return(Id("Keys").Types(Int(), String()).Call(Id("m"))) +fmt.Printf("%#v", c) +// Output: +// return Keys[int, string](m) +``` +```go +c := Type().Id("PredeclaredSignedInteger").Interface( + Union(Int(), Int8(), Int16(), Int32(), Int64()), +) +fmt.Printf("%#v", c) +// Output: +// type PredeclaredSignedInteger interface { +// int | int8 | int16 | int32 | int64 +// } +``` +```go +c := Type().Id("AnyString").Interface( + Op("~").String(), +) +fmt.Printf("%#v", c) +// Output: +// type AnyString interface { +// ~string +// } +``` + # Helpers -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Helpers** [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) **Helpers** [Misc](#misc) [File](#file) ### Func methods -All constructs that accept a variadic list of items are paired with GroupFunc +All constructs that accept a variadic list of items are paired with GroupFunc functions that accept a func(*Group). Use for embedding logic. ```go @@ -746,7 +799,7 @@ fmt.Printf("%#v\n%#v", f("a", true), f("b", false)) ``` # Misc -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) **Misc** [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) **Misc** [File](#file) ### Tag Tag renders a struct tag @@ -799,7 +852,7 @@ fmt.Printf("%#v", c) Line inserts a blank line. ### Clone -Be careful when passing *Statement. Consider the following... +Be careful when passing *Statement. Consider the following... ```go a := Id("a") @@ -815,9 +868,9 @@ fmt.Printf("%#v", c) // } ``` -Id("a") returns a *Statement, which the Call() method appends to twice. To +Id("a") returns a *Statement, which the Call() method appends to twice. To avoid this, use Clone. Clone makes a copy of the Statement, so further tokens can be appended -without affecting the original. +without affecting the original. ```go a := Id("a") @@ -834,10 +887,10 @@ fmt.Printf("%#v", c) ``` ### Cgo -The cgo "C" pseudo-package is a special case, and always renders without a package alias. The -import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with -`File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided, -the import is separated, and preceded by the preamble. +The cgo "C" pseudo-package is a special case, and always renders without a package alias. The +import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with +`File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided, +the import is separated, and preceded by the preamble. ```go f := NewFile("a") @@ -874,13 +927,13 @@ fmt.Printf("%#v", f) // C.myprint(cs) // C.free(unsafe.Pointer(cs)) // } -``` +``` # File -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) **File** +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) **File** File represents a single source file. Package imports are managed -automaticaly by File. +automatically by File. ### NewFile NewFile Creates a new file, with the specified package name. @@ -975,7 +1028,7 @@ fmt.Printf("%#v", f) ### ImportNames ImportNames allows multiple names to be imported as a map. Use the [gennames](gennames) command to -automatically generate a go file containing a map of a selection of package names. +automatically generate a go file containing a map of a selection of package names. ### ImportAlias ImportAlias provides the alias for a package path that should be used in the import block. A @@ -1054,3 +1107,7 @@ fmt.Printf("%#v", f) // pkg_d.E() // } ``` + +### NoFormat +NoFormat can be set to true to disable formatting of the generated source. This may be useful +when performance is critical, and readable code is not required. \ No newline at end of file diff --git a/README.md.tpl b/README.md.tpl index 0099e57..c1a33f9 100644 --- a/README.md.tpl +++ b/README.md.tpl @@ -1,7 +1,4 @@ -[![Build Status](https://travis-ci.org/dave/jennifer.svg?branch=master)](https://travis-ci.org/dave/jennifer) [![Go Report Card](https://goreportcard.com/badge/github.com/dave/jennifer)](https://goreportcard.com/report/github.com/dave/jennifer) -[![codecov](https://img.shields.io/badge/codecov-100%25-brightgreen.svg)](https://codecov.io/gh/dave/jennifer) ![stability-stable](https://img.shields.io/badge/stability-stable-brightgreen.svg) -[![Sourcegraph](https://sourcegraph.com/github.com/dave/jennifer/-/badge.svg)](https://sourcegraph.com/github.com/dave/jennifer?badge) # Jennifer Jennifer is a code generator for Go. @@ -27,9 +24,9 @@ Output: go get -u github.com/dave/jennifer/jen ``` -### Need help? -If you get stuck, have a question, would like a code review, or just want a -chat: I'm happy to help! Feel free to open an issue, email me or mention @dave +### Need help? +If you get stuck, have a question, would like a code review, or just want a +chat: I'm happy to help! Feel free to open an issue, email me or mention @dave in your PR. ### Examples @@ -40,17 +37,17 @@ Jennifer has a comprehensive suite of examples - see [godoc](https://godoc.org/g * [go-contentful-generator](https://github.com/nicolai86/go-contentful-generator) ### Rendering -For testing, a File or Statement can be rendered with the fmt package +For testing, a File or Statement can be rendered with the fmt package using the %#v verb. {{ "ExampleCall_fmt" | example }} -This is not recommended for use in production because any error will cause a -panic. For production use, [File.Render](#render) or [File.Save](#save) are +This is not recommended for use in production because any error will cause a +panic. For production use, [File.Render](#render) or [File.Save](#save) are preferred. # Identifiers -**Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +**Identifiers** [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Id {{ "Id" | doc }} @@ -58,7 +55,7 @@ preferred. {{ "ExampleId" | example }} ### Dot -{{ "Dot" | doc }} +{{ "Dot" | doc }} {{ "ExampleDot" | example }} @@ -79,9 +76,9 @@ preferred. {{ "ExampleList" | example }} # Keywords -[Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) **Keywords** [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) -Simple keywords, predeclared identifiers and built-in functions are self +Simple keywords, predeclared identifiers and built-in functions are self explanatory: | Construct | Name | @@ -99,7 +96,7 @@ Built-in functions take a list of parameters and render them appropriately: Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [Switch, Case](#switch-select), [Return](#return) and [Map](#map) are explained below. # Operators -[Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) **Operators** [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) {{ "Op" | doc }} @@ -112,9 +109,9 @@ Special cases for [If, For](#if-for), [Interface, Struct](#interface-struct), [S {{ "ExampleOp_complex_conditions" | example }} # Braces -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) **Braces** [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) -Several methods render curly braces, summarized below: +Several methods render curly braces, summarized below: | Name | Prefix | Separator | Example | | ------------------------------ | ------------ | --------- | -------------------------------------| @@ -133,7 +130,7 @@ Several methods render curly braces, summarized below: {{ "Block[2:]" | doc }} [See example](#switch-select). ### Interface, Struct -Interface and Struct render the keyword followed by a statement list enclosed +Interface and Struct render the keyword followed by a statement list enclosed by curly braces. {{ "ExampleInterface_empty" | example }} @@ -145,7 +142,7 @@ by curly braces. {{ "ExampleStruct" | example }} # Parentheses -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) **Parentheses** [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) Several methods output parenthesis, summarized below: @@ -185,7 +182,7 @@ Several methods output parenthesis, summarized below: {{ "ExampleAssert" | example }} # Control flow -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) **Control flow** [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### If, For If and For render the keyword followed by a semicolon separated list. @@ -205,7 +202,7 @@ Switch, Select, Case and Block are used to build switch or select statements: {{ "ExampleReturn" | example }} # Collections -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) **Collections** [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Map {{ "Map" | doc }} @@ -239,7 +236,7 @@ Switch, Select, Case and Block are used to build switch or select statements: Note: the items are ordered by key when rendered to ensure repeatable code. # Literals -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) **Literals** [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Lit {{ "Lit" | doc }} @@ -252,7 +249,7 @@ Note: the items are ordered by key when rendered to ensure repeatable code. {{ "ExampleLitFunc" | example }} -For the default constant types (bool, int, float64, string, complex128), Lit +For the default constant types (bool, int, float64, string, complex128), Lit will render the untyped constant. | Code | Output | @@ -263,7 +260,7 @@ will render the untyped constant. | `Lit("foo")` | `"foo"` | | `Lit(0 + 1i)` | `(0 + 1i)` | -For all other built-in types (float32, int8, int16, int32, int64, uint, uint8, +For all other built-in types (float32, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, complex64), Lit will also render the type. | Code | Output | @@ -273,7 +270,7 @@ uint16, uint32, uint64, uintptr, complex64), Lit will also render the type. | `Lit(uint8(0x1))` | `uint8(0x1)` | | `Lit(complex64(0 + 1i))` | `complex64(0 + 1i)` | -The built-in alias types byte and rune need a special case. LitRune and LitByte +The built-in alias types byte and rune need a special case. LitRune and LitByte render rune and byte literals. | Code | Output | @@ -282,7 +279,7 @@ render rune and byte literals. | `LitByte(byte(0x1))` | `byte(0x1)` | # Comments -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Helpers](#helpers) [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) **Comments** [Generics](#generics) [Helpers](#helpers) [Misc](#misc) [File](#file) ### Comment {{ "Comment[:2]" | doc }} @@ -300,11 +297,34 @@ render rune and byte literals. {{ "ExampleCommentf" | example }} +# Generics +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Generics** [Helpers](#helpers) [Misc](#misc) [File](#file) + +It is hoped that with the introduction of generics with Go 1.18, the need to generate code +will be reduced. However, for the sake of completeness, we now support generics including +the `any` and `comparable` predeclared identifiers, and the `Types` and `Union` lists. To +emit the approximation (`~`) token, use `Op("~")`. + +### Types + +{{ "Types" | doc }} + +### Union + +{{ "Union" | doc }} + +### Examples + +{{ "ExampleGenericsTypesDefinition" | example }} +{{ "ExampleGenericsTypesUsage" | example }} +{{ "ExampleGenericsUnion" | example }} +{{ "ExampleGenericsApproximate" | example }} + # Helpers -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) **Helpers** [Misc](#misc) [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) **Helpers** [Misc](#misc) [File](#file) ### Func methods -All constructs that accept a variadic list of items are paired with GroupFunc +All constructs that accept a variadic list of items are paired with GroupFunc functions that accept a func(*Group). Use for embedding logic. {{ "ExampleValuesFunc" | example }} @@ -324,7 +344,7 @@ functions that accept a func(*Group). Use for embedding logic. {{ "ExampleDo" | example }} # Misc -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) **Misc** [File](#file) +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) **Misc** [File](#file) ### Tag {{ "Tag" | doc }} @@ -349,25 +369,25 @@ In lists, nil will produce the same effect. {{ "Line" | doc }} ### Clone -Be careful when passing *Statement. Consider the following... +Be careful when passing *Statement. Consider the following... {{ "ExampleStatement_Clone_broken" | example }} -Id("a") returns a *Statement, which the Call() method appends to twice. To -avoid this, use Clone. {{ "Statement.Clone" | doc }} +Id("a") returns a *Statement, which the Call() method appends to twice. To +avoid this, use Clone. {{ "Statement.Clone" | doc }} {{ "ExampleStatement_Clone_fixed" | example }} ### Cgo -The cgo "C" pseudo-package is a special case, and always renders without a package alias. The -import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with -`File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided, -the import is separated, and preceded by the preamble. +The cgo "C" pseudo-package is a special case, and always renders without a package alias. The +import can be added with `Qual`, `Anon` or by supplying a preamble. The preamble is added with +`File.CgoPreamble` which has the same semantics as [Comment](#comments). If a preamble is provided, +the import is separated, and preceded by the preamble. -{{ "ExampleFile_CgoPreamble" | example }} +{{ "ExampleFile_CgoPreamble" | example }} # File -[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Helpers](#helpers) [Misc](#misc) **File** +[Identifiers](#identifiers) [Keywords](#keywords) [Operators](#operators) [Braces](#braces) [Parentheses](#parentheses) [Control flow](#control-flow) [Collections](#collections) [Literals](#literals) [Comments](#comments) [Generics](#generics) [Helpers](#helpers) [Misc](#misc) **File** {{ "File" | doc }} @@ -401,7 +421,7 @@ the import is separated, and preceded by the preamble. {{ "ExampleFile_ImportName" | example }} ### ImportNames -{{ "File.ImportNames" | doc }} +{{ "File.ImportNames" | doc }} ### ImportAlias {{ "File.ImportAlias" | doc }} @@ -423,3 +443,6 @@ the import is separated, and preceded by the preamble. {{ "File.PackagePrefix" | doc }} {{ "ExampleFile_PackagePrefix" | example }} + +### NoFormat +{{ "File.NoFormat" | doc }} \ No newline at end of file diff --git a/genjen/data.go b/genjen/data.go index ff8b39a..26f52ca 100644 --- a/genjen/data.go +++ b/genjen/data.go @@ -2,7 +2,7 @@ package main var keywords = []string{"break", "default", "func", "select", "chan", "else", "const", "fallthrough", "type", "continue", "var", "goto", "defer", "go", "range"} -var identifiers = []string{"bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "true", "false", "iota", "nil", "err"} +var identifiers = []string{"bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "true", "false", "iota", "nil", "err", "any", "comparable"} var groups = []struct { name string // name of the function / method @@ -304,4 +304,22 @@ var groups = []struct { separator: ",", parameters: []string{}, }, + { + name: "Types", + comment: "renders a comma separated list enclosed by square brackets. Use for type parameters and constraints.", + variadic: true, + opening: "[", + closing: "]", + separator: ",", + parameters: []string{"types"}, + }, + { + name: "Union", + comment: "renders a pipe separated list. Use for union type constraints.", + variadic: true, + opening: "", + closing: "", + separator: "|", + parameters: []string{"types"}, + }, } diff --git a/gennames/hints.go b/gennames/hints.go index 51288f1..0a698c7 100644 --- a/gennames/hints.go +++ b/gennames/hints.go @@ -4,6 +4,7 @@ import ( "fmt" "go/build" "io" + "os" "os/exec" "strings" @@ -53,9 +54,11 @@ func getPackages(goListPath, filter string, standard, novendor bool) (map[string } cmd := exec.Command("go", "list", "-e", "-f", "{{ .Standard }} {{ .ImportPath }} {{ .Name }}", goListPath) + home, _ := os.UserHomeDir() cmd.Env = []string{ fmt.Sprintf("GOPATH=%s", build.Default.GOPATH), fmt.Sprintf("GOROOT=%s", build.Default.GOROOT), + fmt.Sprintf("HOME=%s", home), } if standard { cmd.Dir = filepath.Join(build.Default.GOROOT, "src") diff --git a/go.mod b/go.mod index a9be863..c13f01b 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,14 @@ module github.com/dave/jennifer -go 1.15 +go 1.19 + +require ( + github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14 // indirect + github.com/dave/brenda v1.1.0 // indirect + github.com/dave/courtney v0.3.0 // indirect + github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e // indirect + github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e // indirect + github.com/dave/patsy v0.0.0-20210517141501-957256f50cba // indirect + github.com/dave/rebecca v0.9.1 // indirect + golang.org/x/tools v0.1.8 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dc6f830 --- /dev/null +++ b/go.sum @@ -0,0 +1,42 @@ +github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14 h1:YI1gOOdmMk3xodBao7fehcvoZsEeOyy/cfhlpCSPgM4= +github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14/go.mod h1:Sth2QfxfATb/nW4EsrSi2KyJmbcniZ8TgTaji17D6ms= +github.com/dave/brenda v1.1.0 h1:Sl1LlwXnbw7xMhq3y2x11McFu43AjDcwkllxxgZ3EZw= +github.com/dave/brenda v1.1.0/go.mod h1:4wCUr6gSlu5/1Tk7akE5X7UorwiQ8Rij0SKH3/BGMOM= +github.com/dave/courtney v0.3.0 h1:8aR1os2ImdIQf3Zj4oro+lD/L4Srb5VwGefqZ/jzz7U= +github.com/dave/courtney v0.3.0/go.mod h1:BAv3hA06AYfNUjfjQr+5gc6vxeBVOupLqrColj+QSD8= +github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e h1:l99YKCdrK4Lvb/zTupt0GMPfNbncAGf8Cv/t1sYLOg0= +github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= +github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e h1:xURkGi4RydhyaYR6PzcyHTueQudxY4LgxN1oYEPJHa0= +github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= +github.com/dave/patsy v0.0.0-20210517141501-957256f50cba h1:1o36L4EKbZzazMk8iGC4kXpVnZ6TPxR2mZ9qVKjNNAs= +github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc= +github.com/dave/rebecca v0.9.1 h1:jxVfdOxRirbXL28vXMvUvJ1in3djwkVKXCq339qhBL0= +github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/jen/examples_test.go b/jen/examples_test.go index d6316b5..5eac19c 100644 --- a/jen/examples_test.go +++ b/jen/examples_test.go @@ -1,13 +1,83 @@ package jen_test import ( - "fmt" - "bytes" + "fmt" . "github.com/dave/jennifer/jen" ) +func ExampleGenericsTypesFuncEmpty() { + c := Func().Id("F").TypesFunc(func(group *Group) {}).Params().Block() + fmt.Printf("%#v", c) + // Output: + // func F() {} +} + +func ExampleGenericsTypesFuncNull() { + c := Func().Id("F").TypesFunc(func(group *Group) { + group.Null() + }).Params().Block() + fmt.Printf("%#v", c) + // Output: + // func F() {} +} + +func ExampleGenericsTypesEmpty() { + c := Func().Id("F").Types().Params().Block() + fmt.Printf("%#v", c) + // Output: + // func F() {} +} + +func ExampleGenericsTypesNull() { + c := Func().Id("F").Types(Null()).Params().Block() + fmt.Printf("%#v", c) + // Output: + // func F() {} +} + +func ExampleGenericsTypesDefinition() { + c := Func().Id("Keys").Types( + Id("K").Comparable(), + Id("V").Any(), + ).Params( + Id("m").Map(Id("K")).Id("V"), + ).Index().Id("K").Block() + fmt.Printf("%#v", c) + // Output: + // func Keys[K comparable, V any](m map[K]V) []K {} +} + +func ExampleGenericsTypesUsage() { + c := Return(Id("Keys").Types(Int(), String()).Call(Id("m"))) + fmt.Printf("%#v", c) + // Output: + // return Keys[int, string](m) +} + +func ExampleGenericsUnion() { + c := Type().Id("PredeclaredSignedInteger").Interface( + Union(Int(), Int8(), Int16(), Int32(), Int64()), + ) + fmt.Printf("%#v", c) + // Output: + // type PredeclaredSignedInteger interface { + // int | int8 | int16 | int32 | int64 + // } +} + +func ExampleGenericsApproximate() { + c := Type().Id("AnyString").Interface( + Op("~").String(), + ) + fmt.Printf("%#v", c) + // Output: + // type AnyString interface { + // ~string + // } +} + func ExampleCaseBug() { c := Switch(Id("a")).Block( Case(Lit(1)).Block( @@ -1295,6 +1365,19 @@ func ExampleTag() { // } } +func ExampleTag_withQuotesAndNewline() { + c := Type().Id("foo").Struct( + Id("A").String().Tag(map[string]string{"json": "a"}), + Id("B").Int().Tag(map[string]string{"json": "b", "bar": "the value of\nthe\"bar\" tag"}), + ) + fmt.Printf("%#v", c) + // Output: + // type foo struct { + // A string `json:"a"` + // B int `bar:"the value of\nthe\"bar\" tag" json:"b"` + // } +} + func ExampleNull_and_nil() { c := Func().Id("foo").Params( nil, @@ -1608,3 +1691,16 @@ func ExampleFile_PackagePrefix() { // pkg_d.E() // } } + +func ExampleFile_NoFormat() { + f := NewFile("main") + f.NoFormat = true + + f.Func().Id("main").Params().Block( + Qual("fmt", "Println").Call(Lit("foo")), + ) + fmt.Printf("%q", fmt.Sprintf("%#v", f)) // Special case because Go Examples don't handle multiple newlines well. + + // Output: + // "package main\n\nimport \"fmt\"\n\n\nfunc main () {\nfmt.Println (\"foo\")\n}" +} diff --git a/jen/file.go b/jen/file.go index c19f516..7b2f30a 100644 --- a/jen/file.go +++ b/jen/file.go @@ -49,7 +49,7 @@ func NewFilePathName(packagePath, packageName string) *File { } // File represents a single source file. Package imports are managed -// automaticaly by File. +// automatically by File. type File struct { *Group name string @@ -59,6 +59,9 @@ type File struct { comments []string headers []string cgoPreamble []string + // NoFormat can be set to true to disable formatting of the generated source. This may be useful + // when performance is critical, and readable code is not required. + NoFormat bool // If you're worried about generated package aliases conflicting with local variable names, you // can set a prefix here. Package foo becomes {prefix}_foo. PackagePrefix string diff --git a/jen/generated.go b/jen/generated.go index 3983cf9..30ceb78 100644 --- a/jen/generated.go +++ b/jen/generated.go @@ -1276,6 +1276,110 @@ func (s *Statement) Recover() *Statement { return s } +// Types renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. +func Types(types ...Code) *Statement { + return newStatement().Types(types...) +} + +// Types renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. +func (g *Group) Types(types ...Code) *Statement { + s := Types(types...) + g.items = append(g.items, s) + return s +} + +// Types renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. +func (s *Statement) Types(types ...Code) *Statement { + g := &Group{ + close: "]", + items: types, + multi: false, + name: "types", + open: "[", + separator: ",", + } + *s = append(*s, g) + return s +} + +// TypesFunc renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. +func TypesFunc(f func(*Group)) *Statement { + return newStatement().TypesFunc(f) +} + +// TypesFunc renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. +func (g *Group) TypesFunc(f func(*Group)) *Statement { + s := TypesFunc(f) + g.items = append(g.items, s) + return s +} + +// TypesFunc renders a comma separated list enclosed by square brackets. Use for type parameters and constraints. +func (s *Statement) TypesFunc(f func(*Group)) *Statement { + g := &Group{ + close: "]", + multi: false, + name: "types", + open: "[", + separator: ",", + } + f(g) + *s = append(*s, g) + return s +} + +// Union renders a pipe separated list. Use for union type constraints. +func Union(types ...Code) *Statement { + return newStatement().Union(types...) +} + +// Union renders a pipe separated list. Use for union type constraints. +func (g *Group) Union(types ...Code) *Statement { + s := Union(types...) + g.items = append(g.items, s) + return s +} + +// Union renders a pipe separated list. Use for union type constraints. +func (s *Statement) Union(types ...Code) *Statement { + g := &Group{ + close: "", + items: types, + multi: false, + name: "union", + open: "", + separator: "|", + } + *s = append(*s, g) + return s +} + +// UnionFunc renders a pipe separated list. Use for union type constraints. +func UnionFunc(f func(*Group)) *Statement { + return newStatement().UnionFunc(f) +} + +// UnionFunc renders a pipe separated list. Use for union type constraints. +func (g *Group) UnionFunc(f func(*Group)) *Statement { + s := UnionFunc(f) + g.items = append(g.items, s) + return s +} + +// UnionFunc renders a pipe separated list. Use for union type constraints. +func (s *Statement) UnionFunc(f func(*Group)) *Statement { + g := &Group{ + close: "", + multi: false, + name: "union", + open: "", + separator: "|", + } + f(g) + *s = append(*s, g) + return s +} + // Bool renders the bool identifier. func Bool() *Statement { return newStatement().Bool() @@ -1898,6 +2002,56 @@ func (s *Statement) Err() *Statement { return s } +// Any renders the any identifier. +func Any() *Statement { + // notest + return newStatement().Any() +} + +// Any renders the any identifier. +func (g *Group) Any() *Statement { + // notest + s := Any() + g.items = append(g.items, s) + return s +} + +// Any renders the any identifier. +func (s *Statement) Any() *Statement { + // notest + t := token{ + content: "any", + typ: identifierToken, + } + *s = append(*s, t) + return s +} + +// Comparable renders the comparable identifier. +func Comparable() *Statement { + // notest + return newStatement().Comparable() +} + +// Comparable renders the comparable identifier. +func (g *Group) Comparable() *Statement { + // notest + s := Comparable() + g.items = append(g.items, s) + return s +} + +// Comparable renders the comparable identifier. +func (s *Statement) Comparable() *Statement { + // notest + t := token{ + content: "comparable", + typ: identifierToken, + } + *s = append(*s, t) + return s +} + // Break renders the break keyword. func Break() *Statement { // notest diff --git a/jen/generated_test.go b/jen/generated_test.go index fce14c7..0205220 100644 --- a/jen/generated_test.go +++ b/jen/generated_test.go @@ -7,6 +7,20 @@ import ( ) var gencases = []tc{ + { + desc: `typesfunc group`, + // Don't do this! ListFunc used to kludge Group.TypesFunc usage without + // syntax error. + code: Id("a").ListFunc(func(lg *Group) { lg.TypesFunc(func(cg *Group) { cg.Lit(1) }) }), + expect: `a[1]`, + }, + { + desc: `types group`, + // Don't do this! ListFunc used to kludge Group.Types usage without + // syntax error. + code: Id("a").ListFunc(func(lg *Group) { lg.Types(Lit(1)) }), + expect: `a[1]`, + }, { desc: `bool group`, code: BlockFunc(func(g *Group) { diff --git a/jen/generics.go b/jen/generics.go new file mode 100644 index 0000000..6ad1faf --- /dev/null +++ b/jen/generics.go @@ -0,0 +1 @@ +package jen diff --git a/jen/group.go b/jen/group.go index 0b85c90..39c9ee7 100644 --- a/jen/group.go +++ b/jen/group.go @@ -25,6 +25,10 @@ func (g *Group) isNull(f *File) bool { if g.open != "" || g.close != "" { return false } + return g.isNullItems(f) +} + +func (g *Group) isNullItems(f *File) bool { for _, c := range g.items { if !c.isNull(f) { return false @@ -34,6 +38,10 @@ func (g *Group) isNull(f *File) bool { } func (g *Group) render(f *File, w io.Writer, s *Statement) error { + if g.name == "types" && g.isNullItems(f) { + // Special case for types - if all items are null, don't render the open/close tokens. + return nil + } if g.name == "block" && s != nil { // Special CaseBlock format for then the previous item in the statement // is a Case group or the default keyword. @@ -144,4 +152,3 @@ func (g *Group) RenderWithFile(writer io.Writer, file *File) error { } return nil } - diff --git a/jen/hints.go b/jen/hints.go index 7bc307a..8004e4d 100644 --- a/jen/hints.go +++ b/jen/hints.go @@ -4,270 +4,223 @@ package jen // standardLibraryHints contains package name hints var standardLibraryHints = map[string]string{ - "archive/tar": "tar", - "archive/zip": "zip", - "bufio": "bufio", - "builtin": "builtin", - "bytes": "bytes", - "cmd/asm/internal/arch": "arch", - "cmd/asm/internal/asm": "asm", - "cmd/asm/internal/flags": "flags", - "cmd/asm/internal/lex": "lex", - "cmd/compile/internal/amd64": "amd64", - "cmd/compile/internal/arm": "arm", - "cmd/compile/internal/arm64": "arm64", - "cmd/compile/internal/gc": "gc", - "cmd/compile/internal/mips": "mips", - "cmd/compile/internal/mips64": "mips64", - "cmd/compile/internal/ppc64": "ppc64", - "cmd/compile/internal/s390x": "s390x", - "cmd/compile/internal/ssa": "ssa", - "cmd/compile/internal/syntax": "syntax", - "cmd/compile/internal/test": "test", - "cmd/compile/internal/types": "types", - "cmd/compile/internal/wasm": "wasm", - "cmd/compile/internal/x86": "x86", - "cmd/go/internal/base": "base", - "cmd/go/internal/bug": "bug", - "cmd/go/internal/cache": "cache", - "cmd/go/internal/cfg": "cfg", - "cmd/go/internal/clean": "clean", - "cmd/go/internal/cmdflag": "cmdflag", - "cmd/go/internal/dirhash": "dirhash", - "cmd/go/internal/doc": "doc", - "cmd/go/internal/envcmd": "envcmd", - "cmd/go/internal/fix": "fix", - "cmd/go/internal/fmtcmd": "fmtcmd", - "cmd/go/internal/generate": "generate", - "cmd/go/internal/get": "get", - "cmd/go/internal/help": "help", - "cmd/go/internal/imports": "imports", - "cmd/go/internal/list": "list", - "cmd/go/internal/load": "load", - "cmd/go/internal/modcmd": "modcmd", - "cmd/go/internal/modconv": "modconv", - "cmd/go/internal/modfetch": "modfetch", - "cmd/go/internal/modfetch/codehost": "codehost", - "cmd/go/internal/modfile": "modfile", - "cmd/go/internal/modget": "modget", - "cmd/go/internal/modinfo": "modinfo", - "cmd/go/internal/modload": "modload", - "cmd/go/internal/module": "module", - "cmd/go/internal/mvs": "mvs", - "cmd/go/internal/par": "par", - "cmd/go/internal/run": "run", - "cmd/go/internal/search": "search", - "cmd/go/internal/semver": "semver", - "cmd/go/internal/str": "str", - "cmd/go/internal/test": "test", - "cmd/go/internal/tool": "tool", - "cmd/go/internal/txtar": "txtar", - "cmd/go/internal/version": "version", - "cmd/go/internal/vet": "vet", - "cmd/go/internal/web": "web", - "cmd/go/internal/web2": "web2", - "cmd/go/internal/webtest": "webtest", - "cmd/go/internal/work": "work", - "cmd/internal/bio": "bio", - "cmd/internal/browser": "browser", - "cmd/internal/buildid": "buildid", - "cmd/internal/dwarf": "dwarf", - "cmd/internal/edit": "edit", - "cmd/internal/gcprog": "gcprog", - "cmd/internal/goobj": "goobj", - "cmd/internal/obj": "obj", - "cmd/internal/obj/arm": "arm", - "cmd/internal/obj/arm64": "arm64", - "cmd/internal/obj/mips": "mips", - "cmd/internal/obj/ppc64": "ppc64", - "cmd/internal/obj/s390x": "s390x", - "cmd/internal/obj/wasm": "wasm", - "cmd/internal/obj/x86": "x86", - "cmd/internal/objabi": "objabi", - "cmd/internal/objfile": "objfile", - "cmd/internal/src": "src", - "cmd/internal/sys": "sys", - "cmd/internal/test2json": "test2json", - "cmd/link/internal/amd64": "amd64", - "cmd/link/internal/arm": "arm", - "cmd/link/internal/arm64": "arm64", - "cmd/link/internal/ld": "ld", - "cmd/link/internal/loadelf": "loadelf", - "cmd/link/internal/loadmacho": "loadmacho", - "cmd/link/internal/loadpe": "loadpe", - "cmd/link/internal/mips": "mips", - "cmd/link/internal/mips64": "mips64", - "cmd/link/internal/objfile": "objfile", - "cmd/link/internal/ppc64": "ppc64", - "cmd/link/internal/s390x": "s390x", - "cmd/link/internal/sym": "sym", - "cmd/link/internal/wasm": "wasm", - "cmd/link/internal/x86": "x86", - "cmd/vet/internal/cfg": "cfg", - "cmd/vet/internal/whitelist": "whitelist", - "compress/bzip2": "bzip2", - "compress/flate": "flate", - "compress/gzip": "gzip", - "compress/lzw": "lzw", - "compress/zlib": "zlib", - "container/heap": "heap", - "container/list": "list", - "container/ring": "ring", - "context": "context", - "crypto": "crypto", - "crypto/aes": "aes", - "crypto/cipher": "cipher", - "crypto/des": "des", - "crypto/dsa": "dsa", - "crypto/ecdsa": "ecdsa", - "crypto/elliptic": "elliptic", - "crypto/hmac": "hmac", - "crypto/internal/randutil": "randutil", - "crypto/internal/subtle": "subtle", - "crypto/md5": "md5", - "crypto/rand": "rand", - "crypto/rc4": "rc4", - "crypto/rsa": "rsa", - "crypto/sha1": "sha1", - "crypto/sha256": "sha256", - "crypto/sha512": "sha512", - "crypto/subtle": "subtle", - "crypto/tls": "tls", - "crypto/x509": "x509", - "crypto/x509/pkix": "pkix", - "database/sql": "sql", - "database/sql/driver": "driver", - "debug/dwarf": "dwarf", - "debug/elf": "elf", - "debug/gosym": "gosym", - "debug/macho": "macho", - "debug/pe": "pe", - "debug/plan9obj": "plan9obj", - "encoding": "encoding", - "encoding/ascii85": "ascii85", - "encoding/asn1": "asn1", - "encoding/base32": "base32", - "encoding/base64": "base64", - "encoding/binary": "binary", - "encoding/csv": "csv", - "encoding/gob": "gob", - "encoding/hex": "hex", - "encoding/json": "json", - "encoding/pem": "pem", - "encoding/xml": "xml", - "errors": "errors", - "expvar": "expvar", - "flag": "flag", - "fmt": "fmt", - "go/ast": "ast", - "go/build": "build", - "go/constant": "constant", - "go/doc": "doc", - "go/format": "format", - "go/importer": "importer", - "go/internal/gccgoimporter": "gccgoimporter", - "go/internal/gcimporter": "gcimporter", - "go/internal/srcimporter": "srcimporter", - "go/parser": "parser", - "go/printer": "printer", - "go/scanner": "scanner", - "go/token": "token", - "go/types": "types", - "hash": "hash", - "hash/adler32": "adler32", - "hash/crc32": "crc32", - "hash/crc64": "crc64", - "hash/fnv": "fnv", - "html": "html", - "html/template": "template", - "image": "image", - "image/color": "color", - "image/color/palette": "palette", - "image/draw": "draw", - "image/gif": "gif", - "image/internal/imageutil": "imageutil", - "image/jpeg": "jpeg", - "image/png": "png", - "index/suffixarray": "suffixarray", - "internal/bytealg": "bytealg", - "internal/cpu": "cpu", - "internal/nettrace": "nettrace", - "internal/poll": "poll", - "internal/race": "race", - "internal/singleflight": "singleflight", - "internal/syscall/unix": "unix", - "internal/syscall/windows": "windows", - "internal/syscall/windows/registry": "registry", - "internal/syscall/windows/sysdll": "sysdll", - "internal/testenv": "testenv", - "internal/testlog": "testlog", - "internal/trace": "trace", - "io": "io", - "io/ioutil": "ioutil", - "log": "log", - "log/syslog": "syslog", - "math": "math", - "math/big": "big", - "math/bits": "bits", - "math/cmplx": "cmplx", - "math/rand": "rand", - "mime": "mime", - "mime/multipart": "multipart", - "mime/quotedprintable": "quotedprintable", - "net": "net", - "net/http": "http", - "net/http/cgi": "cgi", - "net/http/cookiejar": "cookiejar", - "net/http/fcgi": "fcgi", - "net/http/httptest": "httptest", - "net/http/httptrace": "httptrace", - "net/http/httputil": "httputil", - "net/http/internal": "internal", - "net/http/pprof": "pprof", - "net/internal/socktest": "socktest", - "net/mail": "mail", - "net/rpc": "rpc", - "net/rpc/jsonrpc": "jsonrpc", - "net/smtp": "smtp", - "net/textproto": "textproto", - "net/url": "url", - "os": "os", - "os/exec": "exec", - "os/signal": "signal", - "os/signal/internal/pty": "pty", - "os/user": "user", - "path": "path", - "path/filepath": "filepath", - "plugin": "plugin", - "reflect": "reflect", - "regexp": "regexp", - "regexp/syntax": "syntax", - "runtime": "runtime", - "runtime/cgo": "cgo", - "runtime/debug": "debug", - "runtime/internal/atomic": "atomic", - "runtime/internal/sys": "sys", - "runtime/pprof": "pprof", - "runtime/pprof/internal/profile": "profile", - "runtime/race": "race", - "runtime/trace": "trace", - "sort": "sort", - "strconv": "strconv", - "strings": "strings", - "sync": "sync", - "sync/atomic": "atomic", - "syscall": "syscall", - "testing": "testing", - "testing/internal/testdeps": "testdeps", - "testing/iotest": "iotest", - "testing/quick": "quick", - "text/scanner": "scanner", - "text/tabwriter": "tabwriter", - "text/template": "template", - "text/template/parse": "parse", - "time": "time", - "unicode": "unicode", - "unicode/utf16": "utf16", - "unicode/utf8": "utf8", - "unsafe": "unsafe", + "archive/tar": "tar", + "archive/zip": "zip", + "bufio": "bufio", + "bytes": "bytes", + "compress/bzip2": "bzip2", + "compress/flate": "flate", + "compress/gzip": "gzip", + "compress/lzw": "lzw", + "compress/zlib": "zlib", + "container/heap": "heap", + "container/list": "list", + "container/ring": "ring", + "context": "context", + "crypto": "crypto", + "crypto/aes": "aes", + "crypto/cipher": "cipher", + "crypto/des": "des", + "crypto/dsa": "dsa", + "crypto/ecdsa": "ecdsa", + "crypto/ed25519": "ed25519", + "crypto/elliptic": "elliptic", + "crypto/hmac": "hmac", + "crypto/internal/boring": "boring", + "crypto/internal/boring/bbig": "bbig", + "crypto/internal/boring/bcache": "bcache", + "crypto/internal/boring/sig": "sig", + "crypto/internal/edwards25519": "edwards25519", + "crypto/internal/edwards25519/field": "field", + "crypto/internal/nistec": "nistec", + "crypto/internal/nistec/fiat": "fiat", + "crypto/internal/randutil": "randutil", + "crypto/internal/subtle": "subtle", + "crypto/md5": "md5", + "crypto/rand": "rand", + "crypto/rc4": "rc4", + "crypto/rsa": "rsa", + "crypto/sha1": "sha1", + "crypto/sha256": "sha256", + "crypto/sha512": "sha512", + "crypto/subtle": "subtle", + "crypto/tls": "tls", + "crypto/x509": "x509", + "crypto/x509/internal/macos": "macOS", + "crypto/x509/pkix": "pkix", + "database/sql": "sql", + "database/sql/driver": "driver", + "debug/buildinfo": "buildinfo", + "debug/dwarf": "dwarf", + "debug/elf": "elf", + "debug/gosym": "gosym", + "debug/macho": "macho", + "debug/pe": "pe", + "debug/plan9obj": "plan9obj", + "embed": "embed", + "embed/internal/embedtest": "embedtest", + "encoding": "encoding", + "encoding/ascii85": "ascii85", + "encoding/asn1": "asn1", + "encoding/base32": "base32", + "encoding/base64": "base64", + "encoding/binary": "binary", + "encoding/csv": "csv", + "encoding/gob": "gob", + "encoding/hex": "hex", + "encoding/json": "json", + "encoding/pem": "pem", + "encoding/xml": "xml", + "errors": "errors", + "expvar": "expvar", + "flag": "flag", + "fmt": "fmt", + "go/ast": "ast", + "go/build": "build", + "go/build/constraint": "constraint", + "go/constant": "constant", + "go/doc": "doc", + "go/doc/comment": "comment", + "go/format": "format", + "go/importer": "importer", + "go/internal/gccgoimporter": "gccgoimporter", + "go/internal/gcimporter": "gcimporter", + "go/internal/srcimporter": "srcimporter", + "go/internal/typeparams": "typeparams", + "go/parser": "parser", + "go/printer": "printer", + "go/scanner": "scanner", + "go/token": "token", + "go/types": "types", + "hash": "hash", + "hash/adler32": "adler32", + "hash/crc32": "crc32", + "hash/crc64": "crc64", + "hash/fnv": "fnv", + "hash/maphash": "maphash", + "html": "html", + "html/template": "template", + "image": "image", + "image/color": "color", + "image/color/palette": "palette", + "image/draw": "draw", + "image/gif": "gif", + "image/internal/imageutil": "imageutil", + "image/jpeg": "jpeg", + "image/png": "png", + "index/suffixarray": "suffixarray", + "internal/abi": "abi", + "internal/buildcfg": "buildcfg", + "internal/bytealg": "bytealg", + "internal/cfg": "cfg", + "internal/cpu": "cpu", + "internal/diff": "diff", + "internal/fmtsort": "fmtsort", + "internal/fuzz": "fuzz", + "internal/goarch": "goarch", + "internal/godebug": "godebug", + "internal/goexperiment": "goexperiment", + "internal/goos": "goos", + "internal/goroot": "goroot", + "internal/goversion": "goversion", + "internal/intern": "intern", + "internal/itoa": "itoa", + "internal/lazyregexp": "lazyregexp", + "internal/lazytemplate": "lazytemplate", + "internal/nettrace": "nettrace", + "internal/obscuretestdata": "obscuretestdata", + "internal/oserror": "oserror", + "internal/pkgbits": "pkgbits", + "internal/poll": "poll", + "internal/profile": "profile", + "internal/race": "race", + "internal/reflectlite": "reflectlite", + "internal/singleflight": "singleflight", + "internal/syscall/execenv": "execenv", + "internal/syscall/unix": "unix", + "internal/sysinfo": "sysinfo", + "internal/testenv": "testenv", + "internal/testlog": "testlog", + "internal/trace": "trace", + "internal/txtar": "txtar", + "internal/unsafeheader": "unsafeheader", + "internal/xcoff": "xcoff", + "io": "io", + "io/fs": "fs", + "io/ioutil": "ioutil", + "log": "log", + "log/syslog": "syslog", + "math": "math", + "math/big": "big", + "math/bits": "bits", + "math/cmplx": "cmplx", + "math/rand": "rand", + "mime": "mime", + "mime/multipart": "multipart", + "mime/quotedprintable": "quotedprintable", + "net": "net", + "net/http": "http", + "net/http/cgi": "cgi", + "net/http/cookiejar": "cookiejar", + "net/http/fcgi": "fcgi", + "net/http/httptest": "httptest", + "net/http/httptrace": "httptrace", + "net/http/httputil": "httputil", + "net/http/internal": "internal", + "net/http/internal/ascii": "ascii", + "net/http/internal/testcert": "testcert", + "net/http/pprof": "pprof", + "net/internal/socktest": "socktest", + "net/mail": "mail", + "net/netip": "netip", + "net/rpc": "rpc", + "net/rpc/jsonrpc": "jsonrpc", + "net/smtp": "smtp", + "net/textproto": "textproto", + "net/url": "url", + "os": "os", + "os/exec": "exec", + "os/exec/internal/fdtest": "fdtest", + "os/signal": "signal", + "os/signal/internal/pty": "pty", + "os/user": "user", + "path": "path", + "path/filepath": "filepath", + "plugin": "plugin", + "reflect": "reflect", + "reflect/internal/example1": "example1", + "reflect/internal/example2": "example2", + "regexp": "regexp", + "regexp/syntax": "syntax", + "runtime": "runtime", + "runtime/cgo": "cgo", + "runtime/debug": "debug", + "runtime/internal/atomic": "atomic", + "runtime/internal/math": "math", + "runtime/internal/sys": "sys", + "runtime/metrics": "metrics", + "runtime/pprof": "pprof", + "runtime/race": "race", + "runtime/trace": "trace", + "sort": "sort", + "strconv": "strconv", + "strings": "strings", + "sync": "sync", + "sync/atomic": "atomic", + "syscall": "syscall", + "testing": "testing", + "testing/fstest": "fstest", + "testing/internal/testdeps": "testdeps", + "testing/iotest": "iotest", + "testing/quick": "quick", + "text/scanner": "scanner", + "text/tabwriter": "tabwriter", + "text/template": "template", + "text/template/parse": "parse", + "time": "time", + "time/tzdata": "tzdata", + "unicode": "unicode", + "unicode/utf16": "utf16", + "unicode/utf8": "utf8", + "unsafe": "unsafe", } diff --git a/jen/jen.go b/jen/jen.go index 4cf480a..39ff743 100644 --- a/jen/jen.go +++ b/jen/jen.go @@ -77,11 +77,17 @@ func (f *File) Render(w io.Writer) error { if _, err := source.Write(body.Bytes()); err != nil { return err } - formatted, err := format.Source(source.Bytes()) - if err != nil { - return fmt.Errorf("Error %s while formatting source:\n%s", err, source.String()) + var output []byte + if f.NoFormat { + output = source.Bytes() + } else { + var err error + output, err = format.Source(source.Bytes()) + if err != nil { + return fmt.Errorf("Error %s while formatting source:\n%s", err, source.String()) + } } - if _, err := w.Write(formatted); err != nil { + if _, err := w.Write(output); err != nil { return err } return nil diff --git a/jen/jen_test.go b/jen/jen_test.go index ade1836..a9b4d6f 100644 --- a/jen/jen_test.go +++ b/jen/jen_test.go @@ -24,6 +24,59 @@ var o2 = Options{ } var cases = []tc{ + { + desc: `union_group`, + code: Type().Id("A").InterfaceFunc(func(g *Group) { + g.Union(Id("A"), Id("B")) + }), + expect: `type A interface{ + A|B + }`, + }, + { + desc: `union_group_func`, + code: Type().Id("A").InterfaceFunc(func(g1 *Group) { + g1.UnionFunc(func(g2 *Group) { + g2.Id("A") + g2.Id("B") + }) + }), + expect: `type A interface{ + A|B + }`, + }, + { + desc: `union`, + code: Type().Id("A").Interface(Union(Id("A"), Id("B"))), + expect: `type A interface{ + A|B + }`, + }, + { + desc: `unionFunc`, + code: Type().Id("A").Interface(UnionFunc(func(g *Group) { + g.Id("A") + g.Id("B") + })), + expect: `type A interface{ + A|B + }`, + }, + { + desc: `types1`, + code: Func().Id("A").Types(Id("K").Comparable(), Id("V").Any()).Params(), + expect: `func A[K comparable, V any]()`, + }, + { + desc: `types2`, + code: Func().Id("A").Types(Id("T1"), Id("T2").Any()).Params(), + expect: `func A[T1, T2 any]()`, + }, + { + desc: `types func`, + code: Func().Id("A").Add(Types(Id("T1"), Id("T2").Any())).Params(), + expect: `func A[T1, T2 any]()`, + }, { desc: `scientific notation`, code: Lit(1e3), diff --git a/jen/tag.go b/jen/tag.go index ad99aaf..2f1baa5 100644 --- a/jen/tag.go +++ b/jen/tag.go @@ -59,7 +59,7 @@ func (t tag) render(f *File, w io.Writer, s *Statement) error { if len(str) > 0 { str += " " } - str += fmt.Sprintf(`%s:"%s"`, k, v) + str += fmt.Sprintf(`%s:%q`, k, v) } if strconv.CanBackquote(str) { diff --git a/jennifer.go b/jennifer.go index 37efad4..0b3e425 100644 --- a/jennifer.go +++ b/jennifer.go @@ -1,9 +1,9 @@ // Package jennifer is a code generator for Go package jennifer -//go:generate go get github.com/dave/jennifer/genjen +//go:generate go install github.com/dave/jennifer/genjen //go:generate genjen -//go:generate go get github.com/dave/jennifer/gennames +//go:generate go install github.com/dave/jennifer/gennames //go:generate gennames -output "jen/hints.go" -package "jen" -name "standardLibraryHints" -standard -novendor -path "./..." -//go:generate go get github.com/dave/rebecca/cmd/becca +//go:generate go install github.com/dave/rebecca/cmd/becca //go:generate becca -package=github.com/dave/jennifer/jen