From e419d78db15952756c65a8633558f38080bd352c Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Mon, 5 Feb 2024 00:38:36 +0100 Subject: [PATCH] more docs and cleanup --- cmd/cmd_print.go | 6 +- cmd/setup.go | 21 +++-- pkg/render/handler.go | 18 ++-- pkg/render/handler_filters.go | 68 +++++++-------- pkg/render/handler_formatter.go | 83 ++++++++---------- pkg/render/handler_signal.go | 2 +- pkg/render/output.go | 10 +++ pkg/render/output_colorized.go | 44 ++++------ pkg/render/output_plain.go | 14 +-- pkg/render/render.go | 149 ++++++++++++++++---------------- pkg/render/render_test.go | 6 +- pkg/render/settings.go | 89 +++++++++++++++---- 12 files changed, 279 insertions(+), 231 deletions(-) create mode 100644 pkg/render/output.go diff --git a/cmd/cmd_print.go b/cmd/cmd_print.go index 9599c45..b262cff 100644 --- a/cmd/cmd_print.go +++ b/cmd/cmd_print.go @@ -13,12 +13,12 @@ var printCommand = &cli.Command{ Usage: "Print environment variables", Before: setup, Action: func(_ context.Context, _ *cli.Command) error { - settings.Interpolate = true + settings.UseInterpolatedValues = true var handlers []render.Handler - if settings.ShowPretty { - handlers = append(handlers, render.FormatHandler) + if settings.FormatOutput { + handlers = append(handlers, render.FormatterHandler) } fmt.Println(render.NewRenderer(*settings, handlers...).Document(env)) diff --git a/cmd/setup.go b/cmd/setup.go index fc3a5d2..a7447c1 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -17,17 +17,16 @@ func setup(_ context.Context, cmd *cli.Command) error { return err } - settings = &render.Settings{ - FilterKeyPrefix: cmd.Root().String("key-prefix"), - FilterGroup: cmd.Root().String("group"), - IncludeDisabled: cmd.Root().Bool("include-commented"), - - ShowPretty: cmd.Root().Bool("pretty"), - ShowBlankLines: cmd.Root().Bool("with-blank-lines"), - ShowComments: cmd.Root().Bool("with-comments"), - ShowGroupBanners: cmd.Root().Bool("with-groups"), - ShowColors: cmd.Root().Bool("colors"), - } + settings = render.NewSettings( + render.WithBlankLines(cmd.Root().Bool("with-blank-lines")), + render.WithComments(cmd.Root().Bool("with-comments")), + render.WithGroupBanners(cmd.Root().Bool("with-groups")), + render.WithFilterGroup(cmd.Root().String("group")), + render.WithFilterKeyPrefix(cmd.Root().String("key-prefix")), + render.WithIncludeDisabled(cmd.Root().Bool("include-commented")), + render.WithFormattedOutput(cmd.Root().Bool("pretty")), + render.WithColors(cmd.Root().Bool("colors")), + ) return nil } diff --git a/pkg/render/handler.go b/pkg/render/handler.go index 382c811..73d43f1 100644 --- a/pkg/render/handler.go +++ b/pkg/render/handler.go @@ -7,23 +7,23 @@ import ( type Handler func(in *HandlerInput) HandlerSignal type HandlerInput struct { - Presenter *Renderer - Previous ast.Statement - Settings Settings - Statement any - Value string + Presenter *Renderer + PreviousStatement ast.Statement + Settings Settings + CurrentStatement any + ReturnValue string } -func (si *HandlerInput) Stop() HandlerSignal { +func (hi *HandlerInput) Stop() HandlerSignal { return Stop } -func (si *HandlerInput) Return(val string) HandlerSignal { - si.Value = val +func (hi *HandlerInput) Return(value string) HandlerSignal { + hi.ReturnValue = value return Return } -func (si *HandlerInput) Continue() HandlerSignal { +func (hi *HandlerInput) Continue() HandlerSignal { return Continue } diff --git a/pkg/render/handler_filters.go b/pkg/render/handler_filters.go index 241dcdb..92e74f5 100644 --- a/pkg/render/handler_filters.go +++ b/pkg/render/handler_filters.go @@ -6,83 +6,83 @@ import ( "github.com/jippi/dottie/pkg/ast" ) -// FilterKeyPrefix will filter out Statements that do not have the +// FilterByKeyPrefix will filter out Statements that do not have the // configured (optional) key prefix -func FilterKeyPrefix(in *HandlerInput) HandlerSignal { +func FilterByKeyPrefix(hi *HandlerInput) HandlerSignal { // Short circuit the filter if there is no KeyPrefix to filter on - if len(in.Settings.FilterKeyPrefix) == 0 { - return in.Continue() + if len(hi.Settings.FilterKeyPrefix) == 0 { + return hi.Continue() } - switch val := in.Statement.(type) { + switch statement := hi.CurrentStatement.(type) { case *ast.Assignment: - if !strings.HasPrefix(val.Name, in.Settings.FilterKeyPrefix) { - return in.Stop() + if !strings.HasPrefix(statement.Name, hi.Settings.FilterKeyPrefix) { + return hi.Stop() } } - return in.Continue() + return hi.Continue() } // FilterComments will filter out Comment statements if they aren't to be included -func FilterComments(in *HandlerInput) HandlerSignal { +func FilterComments(hi *HandlerInput) HandlerSignal { // Short circuit the filter if we allow comments - if in.Settings.WithComments() { - return in.Continue() + if hi.Settings.ShowComments { + return hi.Continue() } - switch in.Statement.(type) { + switch hi.CurrentStatement.(type) { case *ast.Comment: - if !in.Settings.WithComments() { - return in.Stop() + if !hi.Settings.ShowComments { + return hi.Stop() } } - return in.Continue() + return hi.Continue() } // FilterDisabledStatements will filter out Assignment Statements that are // disabled -func FilterDisabledStatements(in *HandlerInput) HandlerSignal { +func FilterDisabledStatements(hi *HandlerInput) HandlerSignal { // Short circuit the filter if we allow disabled statements - if in.Settings.IncludeDisabled { - return in.Continue() + if hi.Settings.IncludeDisabled { + return hi.Continue() } - switch val := in.Statement.(type) { + switch statement := hi.CurrentStatement.(type) { case *ast.Assignment: - if !val.Active && !in.Settings.IncludeDisabled { - return in.Stop() + if !statement.Active && !hi.Settings.IncludeDisabled { + return hi.Stop() } } - return in.Continue() + return hi.Continue() } -// FilterGroupName will filter out Statements that do not +// FilterByGroupName will filter out Statements that do not // belong to the required Group name -func FilterGroupName(in *HandlerInput) HandlerSignal { +func FilterByGroupName(hi *HandlerInput) HandlerSignal { // Short circuit the filter if there is no Group name to filter on - if len(in.Settings.FilterGroup) == 0 { - return in.Continue() + if len(hi.Settings.FilterGroup) == 0 { + return hi.Continue() } - switch val := in.Statement.(type) { + switch statement := hi.CurrentStatement.(type) { case *ast.Assignment: - if !val.BelongsToGroup(in.Settings.FilterGroup) { - return in.Stop() + if !statement.BelongsToGroup(hi.Settings.FilterGroup) { + return hi.Stop() } case *ast.Group: - if !val.BelongsToGroup(in.Settings.FilterGroup) { - return in.Stop() + if !statement.BelongsToGroup(hi.Settings.FilterGroup) { + return hi.Stop() } case *ast.Comment: - if !val.BelongsToGroup(in.Settings.FilterGroup) { - return in.Stop() + if !statement.BelongsToGroup(hi.Settings.FilterGroup) { + return hi.Stop() } } - return in.Continue() + return hi.Continue() } diff --git a/pkg/render/handler_formatter.go b/pkg/render/handler_formatter.go index 85ab8db..7f7a348 100644 --- a/pkg/render/handler_formatter.go +++ b/pkg/render/handler_formatter.go @@ -6,80 +6,71 @@ import ( func NewFormatter() *Renderer { settings := Settings{ - IncludeDisabled: true, - Interpolate: false, - ShowBlankLines: true, - ShowColors: false, - ShowComments: true, - ShowGroupBanners: true, + IncludeDisabled: true, + UseInterpolatedValues: false, + ShowBlankLines: true, + ShowColors: false, + ShowComments: true, + ShowGroupBanners: true, } - return NewRenderer(settings, FormatHandler) + return NewRenderer(settings, FormatterHandler) } -func FormatHandler(in *HandlerInput) HandlerSignal { - switch val := in.Statement.(type) { - // Ignore all existing newlines when doing formatting - // we will be injecting these ourself in other places +// FormatterHandler is responsible for formatting an .env file according +// to our opinionated style. +func FormatterHandler(hi *HandlerInput) HandlerSignal { + switch statement := hi.CurrentStatement.(type) { case *ast.Newline: - return in.Stop() + // Ignore all existing newlines when doing formatting as + // we will be injecting these ourself in other places. + return hi.Stop() case *ast.Group: - output := in.Presenter.Group(val) + output := hi.Presenter.Group(statement) if len(output) == 0 { - return in.Stop() + return hi.Stop() } - res := NewLineBuffer() + buf := NewLineBuffer() - // If the previous line is a newline, don't add another one. + // If the previous line is a Newline, don't add another one. // This could happen if a group is the *first* thing in the document - if !(&ast.Newline{}).Is(in.Previous) && in.Previous != nil { - res.AddNewline() + if hi.PreviousStatement != nil && !hi.PreviousStatement.Is(&ast.Newline{}) { + buf.AddNewline() } - return in.Return( - res. - Add(output). - AddNewline(). - Get(), - ) + return hi.Return(buf.Add(output).AddNewline().Get()) case *ast.Assignment: - output := in.Presenter.Assignment(val) + output := hi.Presenter.Assignment(statement) if len(output) == 0 { - return in.Stop() + return hi.Stop() } - buff := NewLineBuffer() + buf := NewLineBuffer() - // If the assignment belongs to a group, but there are no previous - // then we're the first, so add a newline padding - if val.Group != nil && in.Previous == nil { - buff.AddNewline() + // If the previous Statement was also an Assignment, detect if they should + // be allowed to cuddle (without newline between them) or not. + // + // Statements are only allow cuddle if both have no comments + if statement.Is(hi.PreviousStatement) && (statement.HasComments() || assignmentHasComments(hi.PreviousStatement)) { + buf.AddNewline() } - // Looks like current and previous Statement is both "Assignment" - // which mean they might be too close in the document, so we will - // attempt to inject some new-lines to give them some space - if val.Is(in.Previous) { - // only allow cuddling of assignments if they both have no comments - if val.HasComments() || assignmentHasComments(in.Previous) { - buff.AddNewline() - } - } - - return in.Return(buff.Add(output).Get()) + return hi.Return(buf.Add(output).Get()) } - return in.Continue() + return hi.Continue() } -func assignmentHasComments(stmt ast.Statement) bool { - x, ok := stmt.(*ast.Assignment) +// assignmentHasComments checks if the Statement is an Assignment +// and if it has any comments attached to it +func assignmentHasComments(statement ast.Statement) bool { + assignment, ok := statement.(*ast.Assignment) if !ok { return false } - return x.HasComments() + return assignment.HasComments() } diff --git a/pkg/render/handler_signal.go b/pkg/render/handler_signal.go index 65692a9..0c042b8 100644 --- a/pkg/render/handler_signal.go +++ b/pkg/render/handler_signal.go @@ -14,7 +14,7 @@ var signals = []string{ Return: "RETURN", } -// String returns the string corresponding to the token. +// String returns the string corresponding to the Handler Signal. func (hs HandlerSignal) String() string { s := "" diff --git a/pkg/render/output.go b/pkg/render/output.go new file mode 100644 index 0000000..f89d2f3 --- /dev/null +++ b/pkg/render/output.go @@ -0,0 +1,10 @@ +package render + +import "github.com/jippi/dottie/pkg/ast" + +type Output interface { + GroupBanner(*ast.Group, Settings) string + Assignment(*ast.Assignment, Settings) string + Comment(*ast.Comment, Settings) string + Newline(*ast.Newline, Settings) string +} diff --git a/pkg/render/output_colorized.go b/pkg/render/output_colorized.go index ee11581..e86a92e 100644 --- a/pkg/render/output_colorized.go +++ b/pkg/render/output_colorized.go @@ -7,57 +7,45 @@ import ( "github.com/jippi/dottie/pkg/tui" ) -type Outputter interface { - Group(*ast.Group, Settings) string - Assignment(*ast.Assignment, Settings) string - Comment(*ast.Comment, Settings, bool) string - Newline(*ast.Newline, Settings) string -} - -var _ Outputter = (*Colorized)(nil) - -type Colorized struct{} +var _ Output = (*ColorizedOutput)(nil) -func (c Colorized) Group(group *ast.Group, settings Settings) string { - res := NewLineBuffer() +type ColorizedOutput struct{} +func (ColorizedOutput) GroupBanner(group *ast.Group, settings Settings) string { var buf bytes.Buffer out := tui.Theme.Info.Printer(tui.RendererWithTTY(&buf)) + out.Println("################################################################################") out.ApplyStyle(tui.Bold).Println(group.Name) out.Print("################################################################################") - return res. - Add(buf.String()). - Get() + return buf.String() } -func (c Colorized) Assignment(a *ast.Assignment, settings Settings) string { +func (ColorizedOutput) Assignment(assignment *ast.Assignment, settings Settings) string { var buf bytes.Buffer - if !a.Active { + if !assignment.Active { tui.Theme.Danger.BuffPrinter(&buf).Print("#") } - val := a.Literal + val := assignment.Literal - if settings.Interpolate { - val = a.Interpolated + if settings.UseInterpolatedValues { + val = assignment.Interpolated } - tui.Theme.Primary.BuffPrinter(&buf).Print(a.Name) + tui.Theme.Primary.BuffPrinter(&buf).Print(assignment.Name) tui.Theme.Dark.BuffPrinter(&buf).Print("=") - tui.Theme.Success.BuffPrinter(&buf).Print(a.Quote) + tui.Theme.Success.BuffPrinter(&buf).Print(assignment.Quote) tui.Theme.Warning.BuffPrinter(&buf).Print(val) - tui.Theme.Success.BuffPrinter(&buf).Print(a.Quote) + tui.Theme.Success.BuffPrinter(&buf).Print(assignment.Quote) - return NewLineBuffer(). - Add(buf.String()). - Get() + return buf.String() } -func (r Colorized) Comment(comment *ast.Comment, settings Settings, isAssignmentComment bool) string { +func (ColorizedOutput) Comment(comment *ast.Comment, settings Settings) string { var buf bytes.Buffer out := tui.Theme.Success.BuffPrinter(&buf) @@ -78,7 +66,7 @@ func (r Colorized) Comment(comment *ast.Comment, settings Settings, isAssignment return buf.String() } -func (r Colorized) Newline(newline *ast.Newline, settings Settings) string { +func (ColorizedOutput) Newline(newline *ast.Newline, settings Settings) string { if newline.Blank && !settings.WithBlankLines() { return "" } diff --git a/pkg/render/output_plain.go b/pkg/render/output_plain.go index d8e0009..a5bb0f7 100644 --- a/pkg/render/output_plain.go +++ b/pkg/render/output_plain.go @@ -7,11 +7,11 @@ import ( "github.com/jippi/dottie/pkg/ast" ) -var _ Outputter = (*Plain)(nil) +var _ Output = (*PlainOutput)(nil) -type Plain struct{} +type PlainOutput struct{} -func (c Plain) Group(group *ast.Group, settings Settings) string { +func (PlainOutput) GroupBanner(group *ast.Group, settings Settings) string { out := NewLineBuffer() out.Add("################################################################################") @@ -21,7 +21,7 @@ func (c Plain) Group(group *ast.Group, settings Settings) string { return out.Get() } -func (c Plain) Assignment(a *ast.Assignment, settings Settings) string { +func (PlainOutput) Assignment(a *ast.Assignment, settings Settings) string { var buf bytes.Buffer if !a.Active { @@ -30,7 +30,7 @@ func (c Plain) Assignment(a *ast.Assignment, settings Settings) string { val := a.Literal - if settings.Interpolate { + if settings.UseInterpolatedValues { val = a.Interpolated } @@ -39,11 +39,11 @@ func (c Plain) Assignment(a *ast.Assignment, settings Settings) string { return buf.String() } -func (r Plain) Comment(comment *ast.Comment, settings Settings, isAssignmentComment bool) string { +func (r PlainOutput) Comment(comment *ast.Comment, settings Settings) string { return comment.Value } -func (r Plain) Newline(newline *ast.Newline, settings Settings) string { +func (r PlainOutput) Newline(newline *ast.Newline, settings Settings) string { if newline.Blank && !settings.WithBlankLines() { return "" } diff --git a/pkg/render/render.go b/pkg/render/render.go index 38bcfbf..1397d13 100644 --- a/pkg/render/render.go +++ b/pkg/render/render.go @@ -7,47 +7,48 @@ import ( ) type Renderer struct { - Output Outputter - Previous ast.Statement - Settings Settings - handlers []Handler + Output Output + PreviousStatement ast.Statement + Settings Settings + handlers []Handler } func NewRenderer(settings Settings, additionalHandlers ...Handler) *Renderer { - var output Outputter = Plain{} + var output Output = PlainOutput{} - if settings.WithColors() { - output = Colorized{} + if settings.ShowColors { + output = ColorizedOutput{} } + // Default handlers for filtering down the handlers := append( []Handler{ - FilterKeyPrefix, FilterDisabledStatements, - FilterGroupName, + FilterByKeyPrefix, + FilterByGroupName, FilterComments, }, additionalHandlers..., ) return &Renderer{ - Output: output, - Previous: nil, - Settings: settings, - handlers: handlers, + Output: output, + PreviousStatement: nil, + Settings: settings, + handlers: handlers, } } -func (r *Renderer) Statement(stmt any) string { - in := &HandlerInput{ - Presenter: r, - Previous: r.Previous, - Settings: r.Settings, - Statement: stmt, +func (r *Renderer) Statement(currentStatement any) string { + hi := &HandlerInput{ + Presenter: r, + PreviousStatement: r.PreviousStatement, + Settings: r.Settings, + CurrentStatement: currentStatement, } for _, handler := range r.handlers { - status := handler(in) + status := handler(hi) switch status { // Stop processing the statement and return nothing @@ -56,9 +57,9 @@ func (r *Renderer) Statement(stmt any) string { // Stop processing the statement and return the value from the handler case Return: - r.Previous, _ = stmt.(ast.Statement) + r.PreviousStatement, _ = currentStatement.(ast.Statement) - return in.Value + return hi.ReturnValue // Continue to next handler (or default behavior) case Continue: @@ -69,77 +70,85 @@ func (r *Renderer) Statement(stmt any) string { } } - // Default behavior + // + // Default Statement behavior + // - switch val := stmt.(type) { + switch statement := currentStatement.(type) { case *ast.Document: - r.Previous = val + r.PreviousStatement = statement - return r.Document(val) + return r.Document(statement) case *ast.Group: - r.Previous = val + r.PreviousStatement = statement - return r.Group(val) + return r.Group(statement) case *ast.Comment: - r.Previous = val + r.PreviousStatement = statement - return r.Comment(val, false) + return r.Comment(statement) case *ast.Assignment: - r.Previous = val + r.PreviousStatement = statement - return r.Assignment(val) + return r.Assignment(statement) case *ast.Newline: - r.Previous = val + r.PreviousStatement = statement - return r.Newline(val) + return r.Newline(statement) + + // + // Lists of different statements will be iterated over + // case []*ast.Group: - out := NewLineBuffer() + buf := NewLineBuffer() - for _, group := range val { - if out.AddAndReturnPrinted(r.Statement(group)) { - r.Previous = group + for _, group := range statement { + if buf.AddAndReturnPrinted(r.Statement(group)) { + r.PreviousStatement = group } } - return out.Get() + return buf.Get() case []ast.Statement: - out := NewLineBuffer() + buf := NewLineBuffer() - for _, stmt := range val { - if out.AddAndReturnPrinted(r.Statement(stmt)) { - r.Previous = stmt + for _, stmt := range statement { + if buf.AddAndReturnPrinted(r.Statement(stmt)) { + r.PreviousStatement = stmt } } - return out.Get() + return buf.Get() case []*ast.Comment: - res := NewLineBuffer() - for _, comment := range val { - if res.AddAndReturnPrinted(r.Statement(comment)) { - r.Previous = comment + buf := NewLineBuffer() + for _, comment := range statement { + if buf.AddAndReturnPrinted(r.Statement(comment)) { + r.PreviousStatement = comment } } - return res.Get() + return buf.Get() + + // + // Unrecognized Statement type + // default: - panic(fmt.Sprintf("Unknown statement: %T", val)) + panic(fmt.Sprintf("Unknown statement: %T", statement)) } } -func (r *Renderer) Document(doc *ast.Document) string { - out := NewLineBuffer() - - return out. - Add(r.Statement(doc.Statements)). - Add(r.Statement(doc.Groups)). +func (r *Renderer) Document(document *ast.Document) string { + return NewLineBuffer(). + Add(r.Statement(document.Statements)). + Add(r.Statement(document.Groups)). GetWithEOF() } @@ -149,34 +158,26 @@ func (r *Renderer) Group(group *ast.Group) string { return "" } - res := NewLineBuffer() + buf := NewLineBuffer() - if r.Settings.WithGroupBanners() && len(rendered) > 0 { - res.Add(r.Output.Group(group, r.Settings)) + if r.Settings.ShowGroupBanners && len(rendered) > 0 { + buf.Add(r.Output.GroupBanner(group, r.Settings)) } - return res. - Add(rendered). - Get() + return buf.Add(rendered).Get() } -func (r *Renderer) Assignment(a *ast.Assignment) string { - res := NewLineBuffer() - - return res. - Add(r.Statement(a.Comments)). - Add(r.Output.Assignment(a, r.Settings)). +func (r *Renderer) Assignment(assignment *ast.Assignment) string { + return NewLineBuffer(). + Add(r.Statement(assignment.Comments)). + Add(r.Output.Assignment(assignment, r.Settings)). Get() } -func (r *Renderer) Comment(comment *ast.Comment, isAssignmentComment bool) string { - return r.Output.Comment(comment, r.Settings, isAssignmentComment) +func (r *Renderer) Comment(comment *ast.Comment) string { + return r.Output.Comment(comment, r.Settings) } func (r *Renderer) Newline(newline *ast.Newline) string { return r.Output.Newline(newline, r.Settings) } - -func (r *Renderer) SetOutput(output Outputter) { - r.Output = output -} diff --git a/pkg/render/render_test.go b/pkg/render/render_test.go index a77ac4c..efc92e3 100644 --- a/pkg/render/render_test.go +++ b/pkg/render/render_test.go @@ -40,7 +40,11 @@ func TestFormatter(t *testing.T) { case strings.HasSuffix(file.Name(), ".input.env"): testName := strings.TrimSuffix(file.Name(), ".input.env") - tests = append(tests, testData{name: testName, filename: "test-fixtures/formatter/" + file.Name()}) + test := testData{ + name: testName, + filename: "test-fixtures/formatter/" + file.Name(), + } + tests = append(tests, test) case strings.HasSuffix(file.Name(), ".golden.env"): default: diff --git a/pkg/render/settings.go b/pkg/render/settings.go index 26eda49..de251b5 100644 --- a/pkg/render/settings.go +++ b/pkg/render/settings.go @@ -1,31 +1,86 @@ package render type Settings struct { - FilterKeyPrefix string - FilterGroup string - IncludeDisabled bool + FilterKeyPrefix string + FilterGroup string + IncludeDisabled bool + ShowBlankLines bool + ShowColors bool + ShowComments bool + ShowGroupBanners bool + FormatOutput bool + UseInterpolatedValues bool +} + +type SettingsOption func(*Settings) + +func NewSettings(options ...SettingsOption) *Settings { + settings := &Settings{} + + for _, option := range options { + option(settings) + } + + return settings +} + +func WithFilterKeyPrefix(prefix string) SettingsOption { + return func(s *Settings) { + s.FilterKeyPrefix = prefix + } +} - ShowBlankLines bool - ShowColors bool - ShowComments bool - ShowGroupBanners bool - ShowPretty bool +func WithFilterGroup(name string) SettingsOption { + return func(s *Settings) { + s.FilterGroup = name + } +} + +func WithIncludeDisabled(b bool) SettingsOption { + return func(s *Settings) { + s.IncludeDisabled = b + } +} + +func WithBlankLines(b bool) SettingsOption { + return func(s *Settings) { + s.ShowBlankLines = b + } +} + +func WithColors(b bool) SettingsOption { + return func(s *Settings) { + s.ShowColors = b + } +} - Interpolate bool +func WithComments(b bool) SettingsOption { + return func(s *Settings) { + s.ShowComments = b + } } -func (rs *Settings) WithComments() bool { - return rs.ShowPretty || rs.ShowComments +func WithGroupBanners(b bool) SettingsOption { + return func(s *Settings) { + s.ShowGroupBanners = b + } } -func (rs *Settings) WithGroupBanners() bool { - return rs.ShowPretty || rs.ShowGroupBanners +func WithFormattedOutput(b bool) SettingsOption { + return func(s *Settings) { + s.FormatOutput = b + s.ShowComments = b + s.ShowGroupBanners = b + s.ShowColors = b + } } -func (rs *Settings) WithBlankLines() bool { - return (rs.ShowBlankLines && rs.ShowComments) +func WithInterpolation(b bool) SettingsOption { + return func(s *Settings) { + s.UseInterpolatedValues = b + } } -func (rs *Settings) WithColors() bool { - return rs.ShowColors +func (rs Settings) WithBlankLines() bool { + return !rs.FormatOutput && (rs.ShowBlankLines && rs.ShowComments) }