File: group.go

package info (click to toggle)
golang-github-dave-jennifer 1.4.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 372 kB
  • sloc: makefile: 4
file content (147 lines) | stat: -rw-r--r-- 3,814 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package jen

import (
	"bytes"
	"fmt"
	"go/format"
	"io"
)

// Group represents a list of Code items, separated by tokens with an optional
// open and close token.
type Group struct {
	name      string
	items     []Code
	open      string
	close     string
	separator string
	multi     bool
}

func (g *Group) isNull(f *File) bool {
	if g == nil {
		return true
	}
	if g.open != "" || g.close != "" {
		return false
	}
	for _, c := range g.items {
		if !c.isNull(f) {
			return false
		}
	}
	return true
}

func (g *Group) render(f *File, w io.Writer, s *Statement) error {
	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.
		prev := s.previous(g)
		grp, isGrp := prev.(*Group)
		tkn, isTkn := prev.(token)
		if isGrp && grp.name == "case" || isTkn && tkn.content == "default" {
			g.open = ""
			g.close = ""
		}
	}
	if g.open != "" {
		if _, err := w.Write([]byte(g.open)); err != nil {
			return err
		}
	}
	isNull, err := g.renderItems(f, w)
	if err != nil {
		return err
	}
	if !isNull && g.multi && g.close != "" {
		// For multi-line blocks with a closing token, we insert a new line after the last item (but
		// not if all items were null). This is to ensure that if the statement finishes with a comment,
		// the closing token is not commented out.
		s := "\n"
		if g.separator == "," {
			// We also insert add trailing comma if the separator was ",".
			s = ",\n"
		}
		if _, err := w.Write([]byte(s)); err != nil {
			return err
		}
	}
	if g.close != "" {
		if _, err := w.Write([]byte(g.close)); err != nil {
			return err
		}
	}
	return nil
}

func (g *Group) renderItems(f *File, w io.Writer) (isNull bool, err error) {
	first := true
	for _, code := range g.items {
		if pt, ok := code.(token); ok && pt.typ == packageToken {
			// Special case for package tokens in Qual groups - for dot-imports, the package token
			// will be null, so will not render and will not be registered in the imports block.
			// This ensures all packageTokens that are rendered are registered.
			f.register(pt.content.(string))
		}
		if code == nil || code.isNull(f) {
			// Null() token produces no output but also
			// no separator. Empty() token products no
			// output but adds a separator.
			continue
		}
		if g.name == "values" {
			if _, ok := code.(Dict); ok && len(g.items) > 1 {
				panic("Error in Values: if Dict is used, must be one item only")
			}
		}
		if !first && g.separator != "" {
			// The separator token is added before each non-null item, but not before the first item.
			if _, err := w.Write([]byte(g.separator)); err != nil {
				return false, err
			}
		}
		if g.multi {
			// For multi-line blocks, we insert a new line before each non-null item.
			if _, err := w.Write([]byte("\n")); err != nil {
				return false, err
			}
		}
		if err := code.render(f, w, nil); err != nil {
			return false, err
		}
		first = false
	}
	return first, nil
}

// Render renders the Group to the provided writer.
func (g *Group) Render(writer io.Writer) error {
	return g.RenderWithFile(writer, NewFile(""))
}

// GoString renders the Group for testing. Any error will cause a panic.
func (g *Group) GoString() string {
	buf := bytes.Buffer{}
	if err := g.Render(&buf); err != nil {
		panic(err)
	}
	return buf.String()
}

// RenderWithFile renders the Group to the provided writer, using imports from the provided file.
func (g *Group) RenderWithFile(writer io.Writer, file *File) error {
	buf := &bytes.Buffer{}
	if err := g.render(file, buf, nil); err != nil {
		return err
	}
	b, err := format.Source(buf.Bytes())
	if err != nil {
		return fmt.Errorf("Error %s while formatting source:\n%s", err, buf.String())
	}
	if _, err := writer.Write(b); err != nil {
		return err
	}
	return nil
}