Copilot commented on code in PR #3019:
URL: https://github.com/apache/dubbo-go/pull/3019#discussion_r2330145435


##########
tools/imports-formatter/main.go:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "sort"
+       "strings"
+)
+
+import (
+       "github.com/pkg/errors"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/tools/imports-formatter/constant"
+)
+
+const (
+       GO_FILE_SUFFIX = ".go"
+       GO_ROOT        = "GOROOT"
+       QUOTATION_MARK = "\""
+       PATH_SEPARATOR = "/"
+       IMPORT         = "import"
+       GO_MOD         = "go.mod"
+)
+
+var (
+       blankLine         bool
+       currentWorkDir, _ = os.Getwd()
+       goRoot            = os.Getenv(GO_ROOT) + "/src"
+       endBlocks         = []string{"var", "const", "type", "func"}
+       projectRootPath   string
+       projectName       string
+       goPkgMap          = make(map[string]struct{})
+       outerComments     = make([]string, 0)
+       // record comments between importBlocks and endBlocks
+       innerComments = make([]string, 0)
+       ignorePath    = []string{".git", ".idea", ".github", ".vscode", 
"vendor", "swagger", "docs"}
+       newLine       = false
+       blockCount    = 0
+)
+
+func init() {
+       flag.StringVar(&projectRootPath, "path", currentWorkDir, "the path need 
to be reformatted")
+       flag.StringVar(&projectName, "module", "", "project name, namely module 
name in the go.mod")
+       flag.BoolVar(&blankLine, "bl", true, "if true, it will split different 
import modules with a blank line")
+}
+
+func main() {
+       fmt.Println("imports-formatter:", constant.Version)
+       flag.Parse()
+       var err error
+       projectName, err = getProjectName(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+
+       if os.Getenv(GO_ROOT) == "" {
+               goRoot = generateGoRoot()
+       }
+
+       err = preProcess(goRoot, goPkgMap)
+       if err != nil {
+               panic(err)
+       }
+
+       err = reformatImports(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+}
+
+func getProjectName(path string) (string, error) {
+       if projectName != "" {
+               return projectName, nil
+       }
+
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return "", err
+       }
+
+       for _, fileInfo := range fileInfos {
+               if fileInfo.Name() == GO_MOD {
+                       f, err := 
os.OpenFile(path+PATH_SEPARATOR+fileInfo.Name(), os.O_RDONLY, 0644)
+                       if err != nil {
+                               return "", err
+                       }
+
+                       reader := bufio.NewReader(f)
+                       for {
+                               line, _, err := reader.ReadLine()
+                               if err != nil {
+                                       return "", err
+                               }
+                               lineStr := strings.TrimSpace(string(line))
+                               if strings.HasPrefix(lineStr, "module") {
+                                       return strings.Split(lineStr, " ")[1], 
nil
+                               }
+                       }
+               }
+       }
+
+       return "", err
+}
+
+func preProcess(path string, goPkgMap map[string]struct{}) error {
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return errors.WithStack(err)
+       }
+
+       dirs := make([]os.FileInfo, 0)
+       for _, fileInfo := range fileInfos {
+               if fileInfo.IsDir() {
+                       dirs = append(dirs, fileInfo)
+               } else if strings.HasSuffix(fileInfo.Name(), GO_FILE_SUFFIX) {
+                       goPkgMap[strings.TrimPrefix(path, goRoot+"/")] = 
struct{}{}
+               }
+       }
+
+       for _, dir := range dirs {
+               err := preProcess(path+PATH_SEPARATOR+dir.Name(), goPkgMap)
+               if err != nil {
+                       return errors.WithStack(err)
+               }
+       }
+
+       return nil
+}
+
+func reformatImports(path string) error {
+       fileInfos, err := ioutil.ReadDir(path)

Review Comment:
   Replace deprecated ioutil.ReadDir calls with os.ReadDir. The os.ReadDir 
function returns []os.DirEntry instead of []os.FileInfo, so you'll need to 
update the variable types and method calls accordingly.



##########
tools/imports-formatter/main.go:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "sort"
+       "strings"
+)
+
+import (
+       "github.com/pkg/errors"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/tools/imports-formatter/constant"
+)
+
+const (
+       GO_FILE_SUFFIX = ".go"
+       GO_ROOT        = "GOROOT"
+       QUOTATION_MARK = "\""
+       PATH_SEPARATOR = "/"
+       IMPORT         = "import"
+       GO_MOD         = "go.mod"
+)
+
+var (
+       blankLine         bool
+       currentWorkDir, _ = os.Getwd()
+       goRoot            = os.Getenv(GO_ROOT) + "/src"
+       endBlocks         = []string{"var", "const", "type", "func"}
+       projectRootPath   string
+       projectName       string
+       goPkgMap          = make(map[string]struct{})
+       outerComments     = make([]string, 0)
+       // record comments between importBlocks and endBlocks
+       innerComments = make([]string, 0)
+       ignorePath    = []string{".git", ".idea", ".github", ".vscode", 
"vendor", "swagger", "docs"}
+       newLine       = false
+       blockCount    = 0
+)
+
+func init() {
+       flag.StringVar(&projectRootPath, "path", currentWorkDir, "the path need 
to be reformatted")
+       flag.StringVar(&projectName, "module", "", "project name, namely module 
name in the go.mod")
+       flag.BoolVar(&blankLine, "bl", true, "if true, it will split different 
import modules with a blank line")
+}
+
+func main() {
+       fmt.Println("imports-formatter:", constant.Version)
+       flag.Parse()
+       var err error
+       projectName, err = getProjectName(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+
+       if os.Getenv(GO_ROOT) == "" {
+               goRoot = generateGoRoot()
+       }
+
+       err = preProcess(goRoot, goPkgMap)
+       if err != nil {
+               panic(err)
+       }
+
+       err = reformatImports(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+}
+
+func getProjectName(path string) (string, error) {
+       if projectName != "" {
+               return projectName, nil
+       }
+
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return "", err
+       }
+
+       for _, fileInfo := range fileInfos {
+               if fileInfo.Name() == GO_MOD {
+                       f, err := 
os.OpenFile(path+PATH_SEPARATOR+fileInfo.Name(), os.O_RDONLY, 0644)
+                       if err != nil {
+                               return "", err
+                       }
+
+                       reader := bufio.NewReader(f)
+                       for {
+                               line, _, err := reader.ReadLine()
+                               if err != nil {
+                                       return "", err
+                               }
+                               lineStr := strings.TrimSpace(string(line))
+                               if strings.HasPrefix(lineStr, "module") {
+                                       return strings.Split(lineStr, " ")[1], 
nil
+                               }
+                       }
+               }
+       }
+
+       return "", err
+}
+
+func preProcess(path string, goPkgMap map[string]struct{}) error {
+       fileInfos, err := ioutil.ReadDir(path)

Review Comment:
   Replace deprecated ioutil.ReadDir calls with os.ReadDir. The os.ReadDir 
function returns []os.DirEntry instead of []os.FileInfo, so you'll need to 
update the variable types and method calls accordingly.



##########
tools/imports-formatter/main.go:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"

Review Comment:
   The io/ioutil package is deprecated since Go 1.16. Use io and os packages 
instead. Replace ioutil.ReadDir with os.ReadDir.



##########
tools/imports-formatter/main.go:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "sort"
+       "strings"
+)
+
+import (
+       "github.com/pkg/errors"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/tools/imports-formatter/constant"
+)
+
+const (
+       GO_FILE_SUFFIX = ".go"
+       GO_ROOT        = "GOROOT"
+       QUOTATION_MARK = "\""
+       PATH_SEPARATOR = "/"
+       IMPORT         = "import"
+       GO_MOD         = "go.mod"
+)
+
+var (
+       blankLine         bool
+       currentWorkDir, _ = os.Getwd()
+       goRoot            = os.Getenv(GO_ROOT) + "/src"
+       endBlocks         = []string{"var", "const", "type", "func"}
+       projectRootPath   string
+       projectName       string
+       goPkgMap          = make(map[string]struct{})
+       outerComments     = make([]string, 0)
+       // record comments between importBlocks and endBlocks
+       innerComments = make([]string, 0)
+       ignorePath    = []string{".git", ".idea", ".github", ".vscode", 
"vendor", "swagger", "docs"}
+       newLine       = false
+       blockCount    = 0
+)
+
+func init() {
+       flag.StringVar(&projectRootPath, "path", currentWorkDir, "the path need 
to be reformatted")
+       flag.StringVar(&projectName, "module", "", "project name, namely module 
name in the go.mod")
+       flag.BoolVar(&blankLine, "bl", true, "if true, it will split different 
import modules with a blank line")
+}
+
+func main() {
+       fmt.Println("imports-formatter:", constant.Version)
+       flag.Parse()
+       var err error
+       projectName, err = getProjectName(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+
+       if os.Getenv(GO_ROOT) == "" {
+               goRoot = generateGoRoot()
+       }
+
+       err = preProcess(goRoot, goPkgMap)
+       if err != nil {
+               panic(err)
+       }
+
+       err = reformatImports(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+}
+
+func getProjectName(path string) (string, error) {
+       if projectName != "" {
+               return projectName, nil
+       }
+
+       fileInfos, err := ioutil.ReadDir(path)

Review Comment:
   Replace deprecated ioutil.ReadDir calls with os.ReadDir. The os.ReadDir 
function returns []os.DirEntry instead of []os.FileInfo, so you'll need to 
update the variable types and method calls accordingly.



##########
tools/imports-formatter/main.go:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "sort"
+       "strings"
+)
+
+import (
+       "github.com/pkg/errors"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/tools/imports-formatter/constant"
+)
+
+const (
+       GO_FILE_SUFFIX = ".go"
+       GO_ROOT        = "GOROOT"
+       QUOTATION_MARK = "\""
+       PATH_SEPARATOR = "/"
+       IMPORT         = "import"
+       GO_MOD         = "go.mod"
+)
+
+var (
+       blankLine         bool
+       currentWorkDir, _ = os.Getwd()
+       goRoot            = os.Getenv(GO_ROOT) + "/src"
+       endBlocks         = []string{"var", "const", "type", "func"}
+       projectRootPath   string
+       projectName       string
+       goPkgMap          = make(map[string]struct{})
+       outerComments     = make([]string, 0)
+       // record comments between importBlocks and endBlocks
+       innerComments = make([]string, 0)
+       ignorePath    = []string{".git", ".idea", ".github", ".vscode", 
"vendor", "swagger", "docs"}
+       newLine       = false
+       blockCount    = 0
+)
+
+func init() {
+       flag.StringVar(&projectRootPath, "path", currentWorkDir, "the path need 
to be reformatted")
+       flag.StringVar(&projectName, "module", "", "project name, namely module 
name in the go.mod")
+       flag.BoolVar(&blankLine, "bl", true, "if true, it will split different 
import modules with a blank line")
+}
+
+func main() {
+       fmt.Println("imports-formatter:", constant.Version)
+       flag.Parse()
+       var err error
+       projectName, err = getProjectName(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+
+       if os.Getenv(GO_ROOT) == "" {
+               goRoot = generateGoRoot()
+       }
+
+       err = preProcess(goRoot, goPkgMap)
+       if err != nil {
+               panic(err)
+       }
+
+       err = reformatImports(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+}
+
+func getProjectName(path string) (string, error) {
+       if projectName != "" {
+               return projectName, nil
+       }
+
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return "", err
+       }
+
+       for _, fileInfo := range fileInfos {
+               if fileInfo.Name() == GO_MOD {
+                       f, err := 
os.OpenFile(path+PATH_SEPARATOR+fileInfo.Name(), os.O_RDONLY, 0644)
+                       if err != nil {
+                               return "", err
+                       }
+
+                       reader := bufio.NewReader(f)
+                       for {
+                               line, _, err := reader.ReadLine()
+                               if err != nil {
+                                       return "", err
+                               }
+                               lineStr := strings.TrimSpace(string(line))
+                               if strings.HasPrefix(lineStr, "module") {
+                                       return strings.Split(lineStr, " ")[1], 
nil
+                               }
+                       }
+               }
+       }
+
+       return "", err
+}
+
+func preProcess(path string, goPkgMap map[string]struct{}) error {
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return errors.WithStack(err)
+       }
+
+       dirs := make([]os.FileInfo, 0)
+       for _, fileInfo := range fileInfos {
+               if fileInfo.IsDir() {
+                       dirs = append(dirs, fileInfo)
+               } else if strings.HasSuffix(fileInfo.Name(), GO_FILE_SUFFIX) {
+                       goPkgMap[strings.TrimPrefix(path, goRoot+"/")] = 
struct{}{}
+               }
+       }
+
+       for _, dir := range dirs {
+               err := preProcess(path+PATH_SEPARATOR+dir.Name(), goPkgMap)
+               if err != nil {
+                       return errors.WithStack(err)
+               }
+       }
+
+       return nil
+}
+
+func reformatImports(path string) error {
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return errors.WithStack(err)
+       }
+
+       dirs := make([]os.FileInfo, 0)
+       for _, fileInfo := range fileInfos {
+               if fileInfo.IsDir() && !ignore(fileInfo.Name()) {
+                       dirs = append(dirs, fileInfo)
+               } else if strings.HasSuffix(fileInfo.Name(), GO_FILE_SUFFIX) {
+                       clearData()
+                       newLine = false
+                       err = doReformat(path + PATH_SEPARATOR + 
fileInfo.Name())
+                       if err != nil {
+                               return errors.WithStack(err)
+                       }
+               }
+       }
+
+       for _, dir := range dirs {
+               err := reformatImports(path + "/" + dir.Name())
+               if err != nil {
+                       return errors.WithStack(err)
+               }
+       }
+
+       return nil
+}
+
+func doReformat(filePath string) error {
+       f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
+       defer func(f *os.File) {
+               err := f.Close()
+               if err != nil {
+                       panic(errors.New(filePath + "encounter error:" + 
err.Error()))
+               }
+       }(f)
+       if err != nil {
+               return errors.New("open " + filePath + " encounter error:" + 
err.Error())
+       }
+
+       // Check if this is a generated file (proto, etc.) and skip it
+       scanner := bufio.NewScanner(f)
+       for scanner.Scan() {
+               line := scanner.Text()
+               if strings.Contains(line, "Code generated by") {
+                       // This is a generated file, skip formatting
+                       return nil
+               }
+               // Only check first few lines to avoid reading entire file
+               if strings.Contains(line, "package ") {
+                       break
+               }
+       }
+
+       // Reset file pointer to beginning
+       if _, err := f.Seek(0, 0); err != nil {
+               return errors.New("seek to beginning of " + filePath + " 
encounter error:" + err.Error())
+       }
+
+       reader := bufio.NewReader(f)
+       beginImports := false
+       endImport := false
+       output := make([]byte, 0)
+
+       // processed import(organization) -> original import packages
+       rootImports := make(map[string][]string)
+       internalImports := make(map[string][]string)
+       thirdImports := make(map[string][]string)
+
+       for {
+               if len(outerComments) > 0 {
+                       for _, c := range outerComments {
+                               output = append(output, []byte(c+"\n")...)
+                       }
+                       outerComments = make([]string, 0)
+               }
+
+               line, _, err := reader.ReadLine()
+               if err != nil {
+                       if err == io.EOF {
+                               root := len(rootImports)
+                               internal := len(internalImports)
+                               third := len(thirdImports)
+                               if root > 0 && internal > 0 && third > 0 {
+                                       blockCount = 3
+                               } else if (root > 0 && internal > 0) || (root > 
0 && third > 0) || (internal > 0 && third > 0) {
+                                       blockCount = 2
+                               } else if root > 0 || internal > 0 || third > 0 
{
+                                       blockCount = 1
+                               }
+                               break
+                       }
+                       return errors.New("read line of " + filePath + " 
encounter error:" + err.Error())
+               }
+
+               if endImport {
+                       output = append(output, line...)
+                       output = append(output, []byte("\n")...)
+                       continue
+               }
+
+               // if import blocks end
+               for _, block := range endBlocks {
+                       if strings.HasPrefix(string(line), block) {
+                               endImport = true
+                               beginImports = false
+                               newLine = true
+                               output = refreshImports(output, 
mergeImports(rootImports), false)
+                               output = refreshImports(output, 
mergeImports(thirdImports), blankLine)
+                               output = refreshImports(output, 
mergeImports(internalImports), false)
+                               if len(innerComments) > 0 {
+                                       for _, c := range innerComments {
+                                               output = append(output, 
[]byte(c+"\n")...)
+                                       }
+                               }
+                               break
+                       }
+               }
+
+               lineStr := string(line)
+               if strings.HasPrefix(lineStr, IMPORT) {
+                       beginImports = true
+               }
+
+               orgImportPkg := strings.TrimSpace(lineStr)
+               // single line comment
+               if strings.HasPrefix(orgImportPkg, "//") || 
(strings.HasPrefix(orgImportPkg, "/*") && strings.HasSuffix(orgImportPkg, 
"*/")) {
+                       if beginImports {
+                               innerComments = append(innerComments, lineStr)
+                       } else {
+                               outerComments = append(outerComments, lineStr)
+                       }
+                       continue
+               }
+               // multiple lines comment
+               if strings.HasPrefix(orgImportPkg, "/*") {
+                       if beginImports {
+                               innerComments = append(innerComments, lineStr)
+                               commentLine, _, err := reader.ReadLine()
+                               commentLineStr := string(commentLine)
+                               for err == nil && 
!strings.HasSuffix(strings.TrimSpace(commentLineStr), "*/") {
+                                       innerComments = append(innerComments, 
commentLineStr)
+                                       commentLine, _, err = reader.ReadLine()
+                                       commentLineStr = string(commentLine)
+                               }
+                               if err == nil {
+                                       innerComments = append(innerComments, 
commentLineStr)
+                               } else {
+                                       return errors.New("read line of " + 
filePath + " encounter error:" + err.Error())
+                               }
+                       } else {
+                               outerComments = append(outerComments, lineStr)
+                               commentLine, _, err := reader.ReadLine()
+                               commentLineStr := string(commentLine)
+                               for err == nil && 
!strings.HasSuffix(strings.TrimSpace(commentLineStr), "*/") {
+                                       outerComments = append(outerComments, 
commentLineStr)
+                                       commentLine, _, err = reader.ReadLine()
+                                       commentLineStr = string(commentLine)
+                               }
+                               if err == nil {
+                                       outerComments = append(outerComments, 
commentLineStr)
+                               } else {
+                                       return errors.New("read line of " + 
filePath + " encounter error:" + err.Error())
+                               }
+                       }
+                       continue
+               }
+
+               // collect imports
+               if beginImports && strings.Contains(orgImportPkg, 
QUOTATION_MARK) {
+                       innerComments = innerComments[:0]
+                       // single line import
+                       if strings.HasPrefix(orgImportPkg, IMPORT+" ") {
+                               orgImportPkg = strings.TrimPrefix(orgImportPkg, 
IMPORT+" ")
+                       }
+                       importKey := orgImportPkg
+                       // process those imports that has alias
+                       importKey = unwrapImport(importKey)
+
+                       if _, ok := goPkgMap[importKey]; ok {
+                               // go root import block
+                               cacheImports(rootImports, importKey, 
[]string{orgImportPkg})
+                       } else if importKey == projectName || 
(strings.HasPrefix(importKey, projectName) && len(importKey) > len(projectName) 
&& importKey[len(projectName)] == '/') {
+                               /**
+                               for project a
+                               ****************************
+                               import (
+                                       a
+                                       a/b
+                                       aa
+                               )
+                               ****************************
+                               we need to combine a&a/b, and exclude aa.
+                               importKey == projectName is for a
+                               strings.HasPrefix(importKey, projectName) && 
len(importKey) > len(projectName) && importKey[len(projectName)] == '/' is for 
a/b
+                               if we simply use strings.HasPrefix(importKey, 
projectName), it will recognize a&aa as the same project.
+                               */
+                               // internal imports of the project
+                               cacheImports(internalImports, importKey, 
[]string{orgImportPkg})
+                       } else {
+                               // imports of the third projects
+                               project, importsSegment := "", 
strings.Split(importKey, "/")
+
+                               // like google.golang.org/grpc etc.
+                               if len(importsSegment) == 2 {
+                                       project = 
strings.Join(importsSegment[:2], "/")
+                               } else if len(importsSegment) > 2 {
+                                       project = 
strings.Join(importsSegment[:3], "/")
+                               } else {
+                                       return errors.New("unexpected import 
format: " + orgImportPkg + " in file " + filePath)
+                               }
+                               cacheImports(thirdImports, project, 
[]string{orgImportPkg})
+                       }
+                       continue
+               }
+
+               // to process `import (`
+               if beginImports {
+                       continue
+               }
+
+               output = append(output, line...)
+               output = append(output, []byte("\n")...)
+       }
+
+       if !endImport {
+               output = refreshImports(output, mergeImports(rootImports), 
false)
+               output = refreshImports(output, mergeImports(thirdImports), 
blankLine)
+               output = refreshImports(output, mergeImports(internalImports), 
false)
+               if len(innerComments) > 0 {
+                       for _, c := range innerComments {
+                               output = append(output, []byte(c+"\n")...)
+                       }
+               }
+       }
+
+       outF, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 
0644)
+       if err != nil {
+               return errors.New("open/create" + filePath + " encounter 
error:" + err.Error())
+       }
+       defer func(outF *os.File) {
+               err := outF.Close()
+               if err != nil {
+

Review Comment:
   Empty error handling block. The error from file close should be handled 
properly, either by logging it or returning it to the caller.
   ```suggestion
                        panic(errors.New(filePath + " encounter error on close: 
" + err.Error()))
   ```



##########
tools/imports-formatter/main.go:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "sort"
+       "strings"
+)
+
+import (
+       "github.com/pkg/errors"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/tools/imports-formatter/constant"
+)
+
+const (
+       GO_FILE_SUFFIX = ".go"
+       GO_ROOT        = "GOROOT"
+       QUOTATION_MARK = "\""
+       PATH_SEPARATOR = "/"
+       IMPORT         = "import"
+       GO_MOD         = "go.mod"
+)
+
+var (
+       blankLine         bool
+       currentWorkDir, _ = os.Getwd()
+       goRoot            = os.Getenv(GO_ROOT) + "/src"
+       endBlocks         = []string{"var", "const", "type", "func"}
+       projectRootPath   string
+       projectName       string
+       goPkgMap          = make(map[string]struct{})
+       outerComments     = make([]string, 0)
+       // record comments between importBlocks and endBlocks
+       innerComments = make([]string, 0)
+       ignorePath    = []string{".git", ".idea", ".github", ".vscode", 
"vendor", "swagger", "docs"}
+       newLine       = false
+       blockCount    = 0
+)
+
+func init() {
+       flag.StringVar(&projectRootPath, "path", currentWorkDir, "the path need 
to be reformatted")
+       flag.StringVar(&projectName, "module", "", "project name, namely module 
name in the go.mod")
+       flag.BoolVar(&blankLine, "bl", true, "if true, it will split different 
import modules with a blank line")
+}
+
+func main() {
+       fmt.Println("imports-formatter:", constant.Version)
+       flag.Parse()
+       var err error
+       projectName, err = getProjectName(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+
+       if os.Getenv(GO_ROOT) == "" {
+               goRoot = generateGoRoot()
+       }
+
+       err = preProcess(goRoot, goPkgMap)
+       if err != nil {
+               panic(err)
+       }
+
+       err = reformatImports(projectRootPath)
+       if err != nil {
+               panic(err)
+       }
+}
+
+func getProjectName(path string) (string, error) {
+       if projectName != "" {
+               return projectName, nil
+       }
+
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return "", err
+       }
+
+       for _, fileInfo := range fileInfos {
+               if fileInfo.Name() == GO_MOD {
+                       f, err := 
os.OpenFile(path+PATH_SEPARATOR+fileInfo.Name(), os.O_RDONLY, 0644)
+                       if err != nil {
+                               return "", err
+                       }
+
+                       reader := bufio.NewReader(f)
+                       for {
+                               line, _, err := reader.ReadLine()
+                               if err != nil {
+                                       return "", err
+                               }
+                               lineStr := strings.TrimSpace(string(line))
+                               if strings.HasPrefix(lineStr, "module") {
+                                       return strings.Split(lineStr, " ")[1], 
nil
+                               }
+                       }
+               }
+       }
+
+       return "", err
+}
+
+func preProcess(path string, goPkgMap map[string]struct{}) error {
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return errors.WithStack(err)
+       }
+
+       dirs := make([]os.FileInfo, 0)
+       for _, fileInfo := range fileInfos {
+               if fileInfo.IsDir() {
+                       dirs = append(dirs, fileInfo)
+               } else if strings.HasSuffix(fileInfo.Name(), GO_FILE_SUFFIX) {
+                       goPkgMap[strings.TrimPrefix(path, goRoot+"/")] = 
struct{}{}
+               }
+       }
+
+       for _, dir := range dirs {
+               err := preProcess(path+PATH_SEPARATOR+dir.Name(), goPkgMap)
+               if err != nil {
+                       return errors.WithStack(err)
+               }
+       }
+
+       return nil
+}
+
+func reformatImports(path string) error {
+       fileInfos, err := ioutil.ReadDir(path)
+       if err != nil {
+               return errors.WithStack(err)
+       }
+
+       dirs := make([]os.FileInfo, 0)
+       for _, fileInfo := range fileInfos {
+               if fileInfo.IsDir() && !ignore(fileInfo.Name()) {
+                       dirs = append(dirs, fileInfo)
+               } else if strings.HasSuffix(fileInfo.Name(), GO_FILE_SUFFIX) {
+                       clearData()
+                       newLine = false
+                       err = doReformat(path + PATH_SEPARATOR + 
fileInfo.Name())
+                       if err != nil {
+                               return errors.WithStack(err)
+                       }
+               }
+       }
+
+       for _, dir := range dirs {
+               err := reformatImports(path + "/" + dir.Name())
+               if err != nil {
+                       return errors.WithStack(err)
+               }
+       }
+
+       return nil
+}
+
+func doReformat(filePath string) error {
+       f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
+       defer func(f *os.File) {
+               err := f.Close()
+               if err != nil {
+                       panic(errors.New(filePath + "encounter error:" + 
err.Error()))
+               }
+       }(f)
+       if err != nil {
+               return errors.New("open " + filePath + " encounter error:" + 
err.Error())
+       }
+
+       // Check if this is a generated file (proto, etc.) and skip it
+       scanner := bufio.NewScanner(f)
+       for scanner.Scan() {
+               line := scanner.Text()
+               if strings.Contains(line, "Code generated by") {
+                       // This is a generated file, skip formatting
+                       return nil
+               }
+               // Only check first few lines to avoid reading entire file
+               if strings.Contains(line, "package ") {
+                       break
+               }
+       }
+
+       // Reset file pointer to beginning
+       if _, err := f.Seek(0, 0); err != nil {
+               return errors.New("seek to beginning of " + filePath + " 
encounter error:" + err.Error())
+       }
+
+       reader := bufio.NewReader(f)
+       beginImports := false
+       endImport := false
+       output := make([]byte, 0)
+
+       // processed import(organization) -> original import packages
+       rootImports := make(map[string][]string)
+       internalImports := make(map[string][]string)
+       thirdImports := make(map[string][]string)
+
+       for {
+               if len(outerComments) > 0 {
+                       for _, c := range outerComments {
+                               output = append(output, []byte(c+"\n")...)
+                       }
+                       outerComments = make([]string, 0)
+               }
+
+               line, _, err := reader.ReadLine()
+               if err != nil {
+                       if err == io.EOF {
+                               root := len(rootImports)
+                               internal := len(internalImports)
+                               third := len(thirdImports)
+                               if root > 0 && internal > 0 && third > 0 {
+                                       blockCount = 3
+                               } else if (root > 0 && internal > 0) || (root > 
0 && third > 0) || (internal > 0 && third > 0) {
+                                       blockCount = 2
+                               } else if root > 0 || internal > 0 || third > 0 
{
+                                       blockCount = 1
+                               }
+                               break
+                       }
+                       return errors.New("read line of " + filePath + " 
encounter error:" + err.Error())
+               }
+
+               if endImport {
+                       output = append(output, line...)
+                       output = append(output, []byte("\n")...)
+                       continue
+               }
+
+               // if import blocks end
+               for _, block := range endBlocks {
+                       if strings.HasPrefix(string(line), block) {
+                               endImport = true
+                               beginImports = false
+                               newLine = true
+                               output = refreshImports(output, 
mergeImports(rootImports), false)
+                               output = refreshImports(output, 
mergeImports(thirdImports), blankLine)
+                               output = refreshImports(output, 
mergeImports(internalImports), false)
+                               if len(innerComments) > 0 {
+                                       for _, c := range innerComments {
+                                               output = append(output, 
[]byte(c+"\n")...)
+                                       }
+                               }
+                               break
+                       }
+               }
+
+               lineStr := string(line)
+               if strings.HasPrefix(lineStr, IMPORT) {
+                       beginImports = true
+               }
+
+               orgImportPkg := strings.TrimSpace(lineStr)
+               // single line comment
+               if strings.HasPrefix(orgImportPkg, "//") || 
(strings.HasPrefix(orgImportPkg, "/*") && strings.HasSuffix(orgImportPkg, 
"*/")) {
+                       if beginImports {
+                               innerComments = append(innerComments, lineStr)
+                       } else {
+                               outerComments = append(outerComments, lineStr)
+                       }
+                       continue
+               }
+               // multiple lines comment
+               if strings.HasPrefix(orgImportPkg, "/*") {
+                       if beginImports {
+                               innerComments = append(innerComments, lineStr)
+                               commentLine, _, err := reader.ReadLine()
+                               commentLineStr := string(commentLine)
+                               for err == nil && 
!strings.HasSuffix(strings.TrimSpace(commentLineStr), "*/") {
+                                       innerComments = append(innerComments, 
commentLineStr)
+                                       commentLine, _, err = reader.ReadLine()
+                                       commentLineStr = string(commentLine)
+                               }
+                               if err == nil {
+                                       innerComments = append(innerComments, 
commentLineStr)
+                               } else {
+                                       return errors.New("read line of " + 
filePath + " encounter error:" + err.Error())
+                               }
+                       } else {
+                               outerComments = append(outerComments, lineStr)
+                               commentLine, _, err := reader.ReadLine()
+                               commentLineStr := string(commentLine)
+                               for err == nil && 
!strings.HasSuffix(strings.TrimSpace(commentLineStr), "*/") {
+                                       outerComments = append(outerComments, 
commentLineStr)
+                                       commentLine, _, err = reader.ReadLine()
+                                       commentLineStr = string(commentLine)
+                               }
+                               if err == nil {
+                                       outerComments = append(outerComments, 
commentLineStr)
+                               } else {
+                                       return errors.New("read line of " + 
filePath + " encounter error:" + err.Error())
+                               }
+                       }
+                       continue
+               }
+
+               // collect imports
+               if beginImports && strings.Contains(orgImportPkg, 
QUOTATION_MARK) {
+                       innerComments = innerComments[:0]
+                       // single line import
+                       if strings.HasPrefix(orgImportPkg, IMPORT+" ") {
+                               orgImportPkg = strings.TrimPrefix(orgImportPkg, 
IMPORT+" ")
+                       }
+                       importKey := orgImportPkg
+                       // process those imports that has alias
+                       importKey = unwrapImport(importKey)
+
+                       if _, ok := goPkgMap[importKey]; ok {
+                               // go root import block
+                               cacheImports(rootImports, importKey, 
[]string{orgImportPkg})
+                       } else if importKey == projectName || 
(strings.HasPrefix(importKey, projectName) && len(importKey) > len(projectName) 
&& importKey[len(projectName)] == '/') {
+                               /**
+                               for project a
+                               ****************************
+                               import (
+                                       a
+                                       a/b
+                                       aa
+                               )
+                               ****************************
+                               we need to combine a&a/b, and exclude aa.
+                               importKey == projectName is for a
+                               strings.HasPrefix(importKey, projectName) && 
len(importKey) > len(projectName) && importKey[len(projectName)] == '/' is for 
a/b
+                               if we simply use strings.HasPrefix(importKey, 
projectName), it will recognize a&aa as the same project.
+                               */
+                               // internal imports of the project
+                               cacheImports(internalImports, importKey, 
[]string{orgImportPkg})
+                       } else {
+                               // imports of the third projects
+                               project, importsSegment := "", 
strings.Split(importKey, "/")
+
+                               // like google.golang.org/grpc etc.
+                               if len(importsSegment) == 2 {
+                                       project = 
strings.Join(importsSegment[:2], "/")
+                               } else if len(importsSegment) > 2 {
+                                       project = 
strings.Join(importsSegment[:3], "/")
+                               } else {
+                                       return errors.New("unexpected import 
format: " + orgImportPkg + " in file " + filePath)
+                               }
+                               cacheImports(thirdImports, project, 
[]string{orgImportPkg})
+                       }
+                       continue
+               }
+
+               // to process `import (`
+               if beginImports {
+                       continue
+               }
+
+               output = append(output, line...)
+               output = append(output, []byte("\n")...)
+       }
+
+       if !endImport {
+               output = refreshImports(output, mergeImports(rootImports), 
false)
+               output = refreshImports(output, mergeImports(thirdImports), 
blankLine)
+               output = refreshImports(output, mergeImports(internalImports), 
false)
+               if len(innerComments) > 0 {
+                       for _, c := range innerComments {
+                               output = append(output, []byte(c+"\n")...)
+                       }
+               }
+       }
+
+       outF, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 
0644)
+       if err != nil {
+               return errors.New("open/create" + filePath + " encounter 
error:" + err.Error())
+       }
+       defer func(outF *os.File) {
+               err := outF.Close()
+               if err != nil {
+
+               }
+       }(outF)
+       writer := bufio.NewWriter(outF)
+       _, err = writer.Write(output)
+       if err != nil {
+               return errors.New("write " + filePath + " encounter error:" + 
err.Error())
+       }
+       err = writer.Flush()
+       if err != nil {
+               return errors.New("flush " + filePath + " encounter error:" + 
err.Error())
+       }
+       return nil
+}
+
+func unwrapImport(importStr string) string {
+       // exists alias
+       if !strings.HasPrefix(importStr, QUOTATION_MARK) {
+               importStr = strings.Fields(importStr)[1]
+       }
+       return strings.Trim(importStr, QUOTATION_MARK)
+}
+
+func cacheImports(m map[string][]string, key string, values []string) {
+       if oldValues, ok := m[key]; ok {
+               oldValues = append(oldValues, values...)
+               m[key] = oldValues
+       } else {
+               m[key] = values
+       }
+}
+
+func mergeImports(m map[string][]string) map[string][]string {
+       mergedMap := make(map[string][]string)
+       for key := range m {
+               mergedKeys := make([]string, len(m))

Review Comment:
   The slice is pre-allocated with len(m) but should use capacity 0 since 
elements are appended later. This creates a slice with nil elements that get 
appended to. Change to: mergedKeys := make([]string, 0, len(m))
   ```suggestion
                mergedKeys := make([]string, 0, len(m))
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@dubbo.apache.org
For additional commands, e-mail: notifications-h...@dubbo.apache.org


Reply via email to