From e68b9d00a6e05b3a941f63ffb696f91e554ac5ec Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.3. Signed-off-by: Daniel Baumann --- build/codeformat/formatimports.go | 195 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 build/codeformat/formatimports.go (limited to 'build/codeformat/formatimports.go') diff --git a/build/codeformat/formatimports.go b/build/codeformat/formatimports.go new file mode 100644 index 0000000..c9fc2a2 --- /dev/null +++ b/build/codeformat/formatimports.go @@ -0,0 +1,195 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package codeformat + +import ( + "bytes" + "errors" + "io" + "os" + "sort" + "strings" +) + +var importPackageGroupOrders = map[string]int{ + "": 1, // internal + "code.gitea.io/gitea/": 2, +} + +var errInvalidCommentBetweenImports = errors.New("comments between imported packages are invalid, please move comments to the end of the package line") + +var ( + importBlockBegin = []byte("\nimport (\n") + importBlockEnd = []byte("\n)") +) + +type importLineParsed struct { + group string + pkg string + content string +} + +func parseImportLine(line string) (*importLineParsed, error) { + il := &importLineParsed{content: line} + p1 := strings.IndexRune(line, '"') + if p1 == -1 { + return nil, errors.New("invalid import line: " + line) + } + p1++ + p := strings.IndexRune(line[p1:], '"') + if p == -1 { + return nil, errors.New("invalid import line: " + line) + } + p2 := p1 + p + il.pkg = line[p1:p2] + + pDot := strings.IndexRune(il.pkg, '.') + pSlash := strings.IndexRune(il.pkg, '/') + if pDot != -1 && pDot < pSlash { + il.group = "domain-package" + } + for groupName := range importPackageGroupOrders { + if groupName == "" { + continue // skip internal + } + if strings.HasPrefix(il.pkg, groupName) { + il.group = groupName + } + } + return il, nil +} + +type ( + importLineGroup []*importLineParsed + importLineGroupMap map[string]importLineGroup +) + +func formatGoImports(contentBytes []byte) ([]byte, error) { + p1 := bytes.Index(contentBytes, importBlockBegin) + if p1 == -1 { + return nil, nil + } + p1 += len(importBlockBegin) + p := bytes.Index(contentBytes[p1:], importBlockEnd) + if p == -1 { + return nil, nil + } + p2 := p1 + p + + importGroups := importLineGroupMap{} + r := bytes.NewBuffer(contentBytes[p1:p2]) + eof := false + for !eof { + line, err := r.ReadString('\n') + eof = err == io.EOF + if err != nil && !eof { + return nil, err + } + line = strings.TrimSpace(line) + if line != "" { + if strings.HasPrefix(line, "//") || strings.HasPrefix(line, "/*") { + return nil, errInvalidCommentBetweenImports + } + importLine, err := parseImportLine(line) + if err != nil { + return nil, err + } + importGroups[importLine.group] = append(importGroups[importLine.group], importLine) + } + } + + var groupNames []string + for groupName, importLines := range importGroups { + groupNames = append(groupNames, groupName) + sort.Slice(importLines, func(i, j int) bool { + return strings.Compare(importLines[i].pkg, importLines[j].pkg) < 0 + }) + } + + sort.Slice(groupNames, func(i, j int) bool { + n1 := groupNames[i] + n2 := groupNames[j] + o1 := importPackageGroupOrders[n1] + o2 := importPackageGroupOrders[n2] + if o1 != 0 && o2 != 0 { + return o1 < o2 + } + if o1 == 0 && o2 == 0 { + return strings.Compare(n1, n2) < 0 + } + return o1 != 0 + }) + + formattedBlock := bytes.Buffer{} + for _, groupName := range groupNames { + hasNormalImports := false + hasDummyImports := false + // non-dummy import comes first + for _, importLine := range importGroups[groupName] { + if strings.HasPrefix(importLine.content, "_") { + hasDummyImports = true + } else { + formattedBlock.WriteString("\t" + importLine.content + "\n") + hasNormalImports = true + } + } + // dummy (_ "pkg") comes later + if hasDummyImports { + if hasNormalImports { + formattedBlock.WriteString("\n") + } + for _, importLine := range importGroups[groupName] { + if strings.HasPrefix(importLine.content, "_") { + formattedBlock.WriteString("\t" + importLine.content + "\n") + } + } + } + formattedBlock.WriteString("\n") + } + formattedBlockBytes := bytes.TrimRight(formattedBlock.Bytes(), "\n") + + var formattedBytes []byte + formattedBytes = append(formattedBytes, contentBytes[:p1]...) + formattedBytes = append(formattedBytes, formattedBlockBytes...) + formattedBytes = append(formattedBytes, contentBytes[p2:]...) + return formattedBytes, nil +} + +// FormatGoImports format the imports by our rules (see unit tests) +func FormatGoImports(file string, doWriteFile bool) error { + f, err := os.Open(file) + if err != nil { + return err + } + var contentBytes []byte + { + defer f.Close() + contentBytes, err = io.ReadAll(f) + if err != nil { + return err + } + } + formattedBytes, err := formatGoImports(contentBytes) + if err != nil { + return err + } + if formattedBytes == nil { + return nil + } + if bytes.Equal(contentBytes, formattedBytes) { + return nil + } + + if doWriteFile { + f, err = os.OpenFile(file, os.O_TRUNC|os.O_WRONLY, 0o644) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(formattedBytes) + return err + } + + return err +} -- cgit v1.2.3