Skip to content

Commit

Permalink
add disabled align options
Browse files Browse the repository at this point in the history
  • Loading branch information
4meepo committed Mar 25, 2023
1 parent af1f353 commit 46f2099
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 33 deletions.
25 changes: 15 additions & 10 deletions cmd/tagalign/tagalign.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,32 @@ import (
)

func main() {
var autoSort bool
var fixedOrder string
var align bool
var sort bool
var order string

// Just for declase
flag.BoolVar(&autoSort, "auto-sort", false, "enable auto sort tags")
flag.StringVar(&fixedOrder, "fixed-order", "", "specify the fixed order of tags, the other tags will be sorted by name")
flag.BoolVar(&align, "align", false, "Whether enable tags align. Default is true.")
flag.BoolVar(&sort, "sort", false, "Whether enable tags sort. Default is false.")
flag.StringVar(&order, "order", "", "Specify the order of tags, the other tags will be sorted by name.")

// read from os.Args
args := os.Args
for i, arg := range args {
if arg == "-auto-sort" {
autoSort = true
if arg == "-align" {
align = true
}
if arg == "-fixed-order" {
fixedOrder = args[i+1]
if arg == "-sort" {
sort = true
}
if arg == "-order" {
order = args[i+1]
}
}

var options []tagalign.Option
if autoSort {
options = append(options, tagalign.WithAutoSort(strings.Split(fixedOrder, ",")...))
if sort {
options = append(options, tagalign.WithAlign(align), tagalign.WithSort(strings.Split(order, ",")...))
}

singlechecker.Main(tagalign.NewAnalyzer(options...))
Expand Down
17 changes: 13 additions & 4 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@ func WithMode(mode Mode) Option {
}
}

// WithAutoSort enable auto sort tags.
// Param fixedOrder specify the fixed order of tags, the other tags will be sorted by name.
func WithAutoSort(fixedOrder ...string) Option {
// WithSort enable tags sort.
// fixedOrder specify the order of tags, the other tags will be sorted by name.
// Sory is disabled by default.
func WithSort(fixedOrder ...string) Option {
return func(h *Helper) {
h.autoSort = true
h.sort = true
h.fixedTagOrder = fixedOrder
}
}

// WithAlign configure whether enable tags align.
// Align is enabled by default.
func WithAlign(enabled bool) Option {
return func(h *Helper) {
h.align = enabled
}
}
69 changes: 51 additions & 18 deletions tagalign.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"go/ast"
"go/token"
"log"
"reflect"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -35,25 +36,35 @@ func NewAnalyzer(options ...Option) *analysis.Analyzer {
func Run(pass *analysis.Pass, options ...Option) []Issue {
var issues []Issue
for _, f := range pass.Files {
h := &Helper{mode: StandaloneMode}
h := &Helper{
mode: StandaloneMode,
align: true,
}
for _, opt := range options {
opt(h)
}

if h.align == false && h.sort == false {
// do nothing
return nil
}

ast.Inspect(f, func(n ast.Node) bool {
h.find(pass, n)
return true
})
h.align(pass)
h.Align(pass)
issues = append(issues, h.issues...)
}
return issues
}

type Helper struct {
mode Mode
autoSort bool
fixedTagOrder []string // fixed tag order, the others will be sorted by name.
mode Mode

align bool // whether enable tags align.
sort bool // whether enable tags sort.
fixedTagOrder []string // the order of tags, the other tags will be sorted by name.

singleFields []*ast.Field
consecutiveFieldsGroups [][]*ast.Field // fields in this group, must be consecutive in struct.
Expand Down Expand Up @@ -131,13 +142,13 @@ func (w *Helper) find(pass *analysis.Pass, n ast.Node) {
return
}

func (w *Helper) align(pass *analysis.Pass) {
// sort and align fields groups
func (w *Helper) Align(pass *analysis.Pass) {
// process grouped fields
for _, fields := range w.consecutiveFieldsGroups {
offsets := make([]int, len(fields))

var maxTagNum int
var tagsGroup [][]*structtag.Tag
var tagsGroup, notSortedTagsGroup [][]*structtag.Tag
for i, field := range fields {
offsets[i] = pass.Fset.Position(field.Tag.Pos()).Column
tag, err := strconv.Unquote(field.Tag.Value)
Expand All @@ -152,13 +163,15 @@ func (w *Helper) align(pass *analysis.Pass) {

maxTagNum = max(maxTagNum, tags.Len())

if w.autoSort {
if w.sort {
notSortedTagsGroup = append(notSortedTagsGroup, tags.Tags())
sortBy(w.fixedTagOrder, tags)
}

tagsGroup = append(tagsGroup, tags.Tags())
}

// if w.align{
// record the max length of each column tag
tagMaxLens := make([]int, maxTagNum)

Expand All @@ -177,14 +190,29 @@ func (w *Helper) align(pass *analysis.Pass) {
for i, field := range fields {
tags := tagsGroup[i]

// new new builder
newTagBuilder := strings.Builder{}
for i, tag := range tags {
format := alignFormat(tagMaxLens[i] + 1) // with an extra space
newTagBuilder.WriteString(fmt.Sprintf(format, tag.String()))
var newTagStr string
if w.align {
// if align enabled, align tags.
newTagBuilder := strings.Builder{}
for i, tag := range tags {
format := alignFormat(tagMaxLens[i] + 1) // with an extra space
newTagBuilder.WriteString(fmt.Sprintf(format, tag.String()))
}
newTagStr = newTagBuilder.String()
} else {
// otherwise check if tags order changed
if w.sort && reflect.DeepEqual(notSortedTagsGroup[i], tags) {
// if tags order not changed, do nothing
continue
}
tagsStr := make([]string, len(tags))
for i, tag := range tags {
tagsStr[i] = tag.String()
}
newTagStr = strings.Join(tagsStr, " ")
}

unquoteTag := strings.TrimSpace(newTagBuilder.String())
unquoteTag := strings.TrimSpace(newTagStr)
newTagValue := fmt.Sprintf("`%s`", unquoteTag)
if field.Tag.Value == newTagValue {
// nothing changed
Expand Down Expand Up @@ -228,7 +256,7 @@ func (w *Helper) align(pass *analysis.Pass) {
}
}

// sort single fields
// process single fields
for _, field := range w.singleFields {
tag, err := strconv.Unquote(field.Tag.Value)
if err != nil {
Expand All @@ -239,11 +267,16 @@ func (w *Helper) align(pass *analysis.Pass) {
if err != nil {
continue
}

if w.autoSort {
originalTags := append([]*structtag.Tag(nil), tags.Tags()...)
if w.sort {
sortBy(w.fixedTagOrder, tags)
}

if reflect.DeepEqual(originalTags, tags.Tags()) {
// if tags order not changed, do nothing
continue
}

newTagValue := fmt.Sprintf("`%s`", tags.String())
if field.Tag.Value == newTagValue {
// nothing changed
Expand Down
10 changes: 9 additions & 1 deletion tagalign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestAnalyzer(t *testing.T) {
}
func TestAnalyzerWithOrder(t *testing.T) {
// sort with fixed order
a := NewAnalyzer(WithAutoSort("json", "yaml", "xml"))
a := NewAnalyzer(WithSort("json", "yaml", "xml"))
sort, err := filepath.Abs("testdata/sort")
assert.NoError(t, err)
analysistest.Run(t, sort, a)
Expand All @@ -41,3 +41,11 @@ func Test_sortByFixedOrder(t *testing.T) {
assert.Equal(t, "gorm", tags.Tags()[4].Key)
assert.Equal(t, "zip", tags.Tags()[5].Key)
}

func Test_disableAlign(t *testing.T) {
// test disable align but enable sort
a := NewAnalyzer(WithAlign(false), WithSort("xml", "json", "yaml"))
sort, err := filepath.Abs("testdata/noalign")
assert.NoError(t, err)
analysistest.Run(t, sort, a)
}
12 changes: 12 additions & 0 deletions testdata/noalign/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package noalign

type SortOnlyExample struct {
// not aligned but sorted, should not be reported
Foo int `xml:"baz" json:"foo,omitempty" yaml:"bar" binding:"required" gorm:"column:foo" validate:"required" zip:"foo" `
Bar int `xml:"bar" json:"bar,omitempty" yaml:"foo" gorm:"column:bar" validate:"required" zip:"bar" `
FooBar int `xml:"bar" json:"bar,omitempty" yaml:"foo" gorm:"column:bar" `
// aligned but not sorted, should be reported
BarFoo int `xml:"bar" yaml:"foo" json:"bar,omitempty" gorm:"column:bar" validate:"required" zip:"bar"` // want `xml:"bar" json:"bar,omitempty" yaml:"foo" gorm:"column:bar" validate:"required" zip:"bar"`
// not aligned but sorted, should not be reported
FooBarFoo int `xml:"bar" json:"bar,omitempty" yaml:"foo" gorm:"column:bar" validate:"required" zip:"bar"`
}

0 comments on commit 46f2099

Please sign in to comment.