Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/implement formatter #291

Merged
merged 29 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cff6dea
acl formatting
ysugimoto Mar 25, 2024
5aaf5b9
wip formatter
ysugimoto Mar 25, 2024
cf8c541
Merge branch 'improve/trailing-comment-parsing' into feature/formatter
ysugimoto Mar 26, 2024
005b85d
WIP statement and expression formatting
ysugimoto Mar 26, 2024
00b575d
Merge remote-tracking branch 'origin/improve/trailing-comment-parsing…
ysugimoto Mar 26, 2024
6d94e0e
WIP statement formatter testing
ysugimoto Mar 26, 2024
1eb5733
Merge remote-tracking branch 'origin/improve/trailing-comment-parsing…
ysugimoto Mar 26, 2024
8ef231f
finished statement parging
ysugimoto Mar 26, 2024
225185e
finished basic implementation
ysugimoto Mar 27, 2024
f6f548d
modify chunking, documentation
ysugimoto Mar 27, 2024
2b3ae83
linting
ysugimoto Mar 27, 2024
5bbdd55
Merge remote-tracking branch 'origin/main' into feature/formatter
ysugimoto Mar 28, 2024
8530335
keep empty lines
ysugimoto Mar 28, 2024
b930a92
add empty line for declaration properties
ysugimoto Mar 30, 2024
33dd59d
implement line stack
ysugimoto Mar 30, 2024
2ce1cdd
chore: add Leading comment
ysugimoto Mar 31, 2024
aa0688a
add line struct and slice
ysugimoto Apr 1, 2024
d4e8194
formatted as expect for my production vcl
ysugimoto Apr 2, 2024
7db5deb
fix config test
ysugimoto Apr 2, 2024
7e03cb5
commenting, add should_use_unset config
ysugimoto Apr 2, 2024
ed3d0a7
documentation
ysugimoto Apr 2, 2024
981697c
tweak documentation
ysugimoto Apr 2, 2024
c440bc1
fix complecated expression including comments
ysugimoto Apr 3, 2024
a2f4bae
refactor a little
ysugimoto Apr 3, 2024
bb2deee
fix prefix expression formatting and add indent_case_labels rule
ysugimoto Apr 4, 2024
a0f6e16
merge main
ysugimoto Apr 29, 2024
7ba2ae0
wip merge and refactoring
ysugimoto Apr 29, 2024
55668b9
fix tests with complex comments
ysugimoto Apr 30, 2024
88748f3
Merge pull request #304 from ysugimoto/feature/implement-formatter-merge
ysugimoto Apr 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ __generator__/*.csv
debugger.log
debug.log
request.json

.falco.yml
.falco.yaml
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Subcommands:
stats : Analyze VCL statistics
simulate : Run simulator server with provided VCLs
test : Run local testing for provided VCLs
fmt : Run formatter for provided VCLs


See subcommands help with:
falco [subcommand] -h
Expand Down Expand Up @@ -93,6 +95,14 @@ that you improve your VCL more robustly by passing the linter.

See [linter documentation](https://github.com/ysugimoto/falco/blob/main/docs/linter.md) in detail.

## Formatter

Format provided VCL by our recommended styles.
Currently we have a few options to control formatting style like [biomejs](https://github.com/biomejs/biome).
Through the formatter, your VCL codes have unified format even multiple people are maintaining VCL.

See [formatter documentation](./docs/formatter.md) in detail.

## Local Simulator / VCL Debugger

`falco` has self-implemented interpreter for running VCL program locally.
Expand Down
11 changes: 6 additions & 5 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ type Expression interface {

// Meta struct of all nodes
type Meta struct {
Token token.Token
Leading Comments
Trailing Comments
Infix Comments
Nest int
Token token.Token
Leading Comments
Trailing Comments
Infix Comments
Nest int
PreviousEmptyLines int
}

func (m *Meta) LeadingComment() string {
Expand Down
3 changes: 2 additions & 1 deletion ast/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ type Comment struct {
// If this flag is false, it should be treated as trailing comment
// because the comment presents on the same line.
// Otherwise, this flag is true, it should be the leading comment for a next token.
PrefixedLineFeed bool
PrefixedLineFeed bool
PreviousEmptyLines int
}

func (c *Comment) String() string {
Expand Down
5 changes: 3 additions & 2 deletions ast/if_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

type IfStatement struct {
*Meta
Keyword string
Condition Expression
Consequence *BlockStatement
Another []*IfStatement
Expand All @@ -19,15 +20,15 @@ func (i *IfStatement) String() string {
var buf bytes.Buffer

buf.WriteString(i.LeadingComment())
buf.WriteString(indent(i.Nest) + "if (")
buf.WriteString(indent(i.Nest) + i.Keyword + " (")
buf.WriteString(i.Condition.String())
buf.WriteString(") ")
buf.WriteString(i.Consequence.String())

for _, a := range i.Another {
buf.WriteString("\n")
buf.WriteString(a.LeadingComment())
buf.WriteString(indent(i.Nest) + "else if (")
buf.WriteString(indent(i.Nest) + a.Keyword + " (")
buf.WriteString(a.Condition.String())
buf.WriteString(") ")
buf.WriteString(a.Consequence.String())
Expand Down
6 changes: 4 additions & 2 deletions ast/if_statement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import (

func TestIfStatement(t *testing.T) {
ifs := &IfStatement{
Meta: New(T, 0, comments("// This is comment"), comments("/* This is comment */")),
Keyword: "if",
Meta: New(T, 0, comments("// This is comment"), comments("/* This is comment */")),
Condition: &Ident{
Meta: New(T, 0),
Value: "req.http.Host",
},
Another: []*IfStatement{
{
Meta: New(T, 0, comments("// This is comment"), comments("/* This is comment */")),
Keyword: "else if",
Meta: New(T, 0, comments("// This is comment"), comments("/* This is comment */")),
Condition: &Ident{
Meta: New(T, 0),
Value: "req.http.Host",
Expand Down
26 changes: 22 additions & 4 deletions cmd/falco/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func printHelp(cmd string) {
printTestHelp()
case subcommandLint:
printLintHelp()
case subcommandFormat:
printFormatHelp()
default:
printGlobalHelp()
}
Expand Down Expand Up @@ -46,6 +48,7 @@ Subcommands:
stats : Analyze VCL statistics
simulate : Run simulator server with provided VCLs
test : Run local testing for provided VCLs
fmt : Run formatter for provided VCLs

See subcommands help with:
falco [subcommand] -h
Expand Down Expand Up @@ -91,7 +94,7 @@ Linting with terraform:
func printSimulateHelp() {
writeln(white, strings.TrimSpace(`
Usage:
falco simulate [flags]
falco simulate [flags] file

Flags:
-I, --include_path : Add include path
Expand All @@ -113,7 +116,7 @@ Local debugger example:
func printStatsHelp() {
writeln(white, strings.TrimSpace(`
Usage:
falco stats [flags]
falco stats [flags] file

Flags:
-I, --include_path : Add include path
Expand Down Expand Up @@ -150,13 +153,12 @@ Local testing example:
func printLintHelp() {
writeln(white, strings.TrimSpace(`
Usage:
falco lint [flags]
falco lint [flags] file

Flags:
-I, --include_path : Add include path
-h, --help : Show this help
-r, --remote : Connect with Fastly API
-V, --version : Display build version
-v : Output lint warnings (verbose)
-vv : Output all lint results (very verbose)
-json : Output results as JSON (very verbose)
Expand All @@ -165,3 +167,19 @@ Simple linting with very verbose example:
falco lint -I . -vv /path/to/vcl/main.vcl
`))
}

func printFormatHelp() {
writeln(white, strings.TrimSpace(`
Usage:
falco fmt [flags] ...files

Flags:
-h, --help : Show this help
-w, --write : Overwrite format result

files argument accepts glob file patterns

Simple format example:
falco fmt /path/to/vcl/main.vcl
`))
}
25 changes: 23 additions & 2 deletions cmd/falco/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
subcommandSimulate = "simulate"
subcommandStats = "stats"
subcommandTest = "test"
subcommandFormat = "fmt"
)

func write(c *color.Color, format string, args ...interface{}) {
Expand Down Expand Up @@ -88,10 +89,17 @@ func main() {
}
action = c.Commands.At(1)
case subcommandSimulate, subcommandLint, subcommandStats, subcommandTest:
// "lint", "simulate", "stats" and "test" command provides single file of service,
// "lint", "simulate", "stats", and "test" command provides single file of service,
// then resolvers size is always 1
resolvers, err = resolver.NewFileResolvers(c.Commands.At(1), c.IncludePaths)
action = c.Commands.At(0)
case subcommandFormat:
// "fmt" command accepts multiple target files
resolvers, err = resolver.NewGlobResolver(c.Commands[1:]...)
action = c.Commands.At(0)
if len(resolvers) == 0 {
err = fmt.Errorf("No input files speficied")
}
case "":
printHelp("")
os.Exit(1)
Expand All @@ -105,7 +113,8 @@ func main() {
}
}

if c.Remote {
// No need to use remove object on fmt command
if action != subcommandFormat && c.Remote {
if !c.Json {
writeln(cyan, "Remote option supplied. Fetching snippets from Fastly.")
}
Expand Down Expand Up @@ -154,6 +163,8 @@ func main() {
exitErr = runSimulate(runner, v)
case subcommandStats:
exitErr = runStats(runner, v)
case subcommandFormat:
exitErr = runFormat(runner, v)
default:
exitErr = runLint(runner, v)
}
Expand Down Expand Up @@ -377,3 +388,13 @@ func runTest(runner *Runner, rslv resolver.Resolver) error {
return nil
}
}

func runFormat(runner *Runner, rslv resolver.Resolver) error {
if err := runner.Format(rslv); err != nil {
if err != ErrParser {
writeln(red, err.Error())
}
return ErrExit
}
return nil
}
32 changes: 32 additions & 0 deletions cmd/falco/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strings"

"github.com/fatih/color"
Expand All @@ -12,6 +14,7 @@ import (
"github.com/ysugimoto/falco/config"
"github.com/ysugimoto/falco/context"
"github.com/ysugimoto/falco/debugger"
"github.com/ysugimoto/falco/formatter"
"github.com/ysugimoto/falco/interpreter"
icontext "github.com/ysugimoto/falco/interpreter/context"
"github.com/ysugimoto/falco/lexer"
Expand Down Expand Up @@ -485,3 +488,32 @@ func (r *Runner) Test(rslv resolver.Resolver) (*tester.TestFactory, error) {
r.message(white, " Done.\n")
return factory, nil
}

func (r *Runner) Format(rslv resolver.Resolver) error {
main, err := rslv.MainVCL()
if err != nil {
return err
}
vcl, err := r.parseVCL(main.Name, main.Data)
if err != nil {
return err
}

formatted := formatter.New(r.config.Format).Format(vcl)
var w io.Writer
if r.config.Format.Overwrite {
writeln(cyan, "Formatted %s.", main.Name)
fp, err := os.OpenFile(main.Name, os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
return errors.WithStack(err)
}
defer fp.Close()
w = fp
} else {
w = os.Stdout
}
if _, err := io.Copy(w, formatted); err != nil {
return err
}
return nil
}
39 changes: 33 additions & 6 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ var (
configurationFiles = []string{".falco.yaml", ".falco.yml"}
)

// Formatting value constants
const (
IndentStyleSpace = "space"
IndentStyleTab = "tab"
CommentStyleNone = "none"
CommentStyleSlash = "slash"
CommentStyleSharp = "sharp"
)

type OverrideBackend struct {
Host string `yaml:"host"`
SSL bool `yaml:"ssl" default:"true"`
Expand Down Expand Up @@ -47,6 +56,28 @@ type TestConfig struct {
OverrideRequest *RequestConfig
}

// Format configuration
type FormatConfig struct {
// CLI options
Overwrite bool `cli:"w,write" default:"false"`

// Formatter options
IndentWidth int `yaml:"indent_width" default:"2"`
TrailingCommentWidth int `yaml:"trailing_comment_width" default:"2"`
IndentStyle string `yaml:"indent_style" default:"space"`
LineWidth int `yaml:"line_width" default:"120"`
ExplicitStringConat bool `yaml:"explicit_string_concat" default:"false"`
SortDeclarationProperty bool `yaml:"sort_declaration_property" default:"false"`
AlignDeclarationProperty bool `yaml:"align_declaration_property" default:"false"`
ElseIf bool `yaml:"else_if" default:"false"`
AlwaysNextLineElseIf bool `yaml:"always_next_line_else_if" default:"false"`
ReturnStatementParenthesis bool `yaml:"return_statement_parenthesis" default:"true"`
SortDeclaration bool `yaml:"sort_declaration" defaul:"false"`
AlignTrailingComment bool `yaml:"align_trailing_comment" default:"false"`
CommentStyle string `yaml:"comment_style" default:"none"`
ShouldUseUnset bool `yaml:"should_use_unset" default:"false"`
Comment on lines +66 to +80
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are all formatting rules, see #274

}

type Config struct {
// Root configurations
IncludePaths []string `cli:"I,include_path" yaml:"include_paths"`
Expand Down Expand Up @@ -77,6 +108,8 @@ type Config struct {
Simulator *SimulatorConfig `yaml:"simulator"`
// Testing configuration
Testing *TestConfig `yaml:"testing"`
// Format configuration
Format *FormatConfig `yaml:"format"`
}

func New(args []string) (*Config, error) {
Expand All @@ -92,12 +125,6 @@ func New(args []string) (*Config, error) {

c := &Config{
OverrideBackends: make(map[string]*OverrideBackend),
// Simulator: &SimulatorConfig{
// OverrideRequest: &RequestConfig{},
// },
// Testing: &TestConfig{
// OverrideRequest: &RequestConfig{},
// },
}
if err := twist.Mix(c, options...); err != nil {
return nil, errors.WithStack(err)
Expand Down
15 changes: 15 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ func TestConfigFromCLI(t *testing.T) {
IncludePaths: []string{"."},
OverrideRequest: &RequestConfig{},
},
Format: &FormatConfig{
IndentWidth: 2,
TrailingCommentWidth: 2,
LineWidth: 120,
IndentStyle: "space",
ExplicitStringConat: false,
SortDeclarationProperty: false,
AlignDeclarationProperty: false,
ElseIf: false,
ReturnStatementParenthesis: true,
SortDeclaration: false,
AlignTrailingComment: false,
CommentStyle: "none",
ShouldUseUnset: false,
},
OverrideBackends: make(map[string]*OverrideBackend),
}

Expand Down
Loading
Loading