Repository: goplus/gop
Pull request #1015 (2021-12-02 13:54:25)

gop fmt --smart


Patch:
diff --git a/cmd/internal/gopfmt/fmt.go b/cmd/internal/gopfmt/fmt.go
index 164085a1..ae362467 100644
--- a/cmd/internal/gopfmt/fmt.go
+++ b/cmd/internal/gopfmt/fmt.go
@@ -30,17 +30,21 @@ import (
 
 	"github.com/goplus/gop/cmd/internal/base"
 	"github.com/goplus/gop/format"
+
+	xformat "github.com/goplus/gop/x/format"
 )
 
 // Cmd - gop go
 var Cmd = &base.Command{
-	UsageLine: "gop fmt [-n] path ...",
+	UsageLine: "gop fmt [-n --smart --mvgo] path ...",
 	Short:     "Format Go+ packages",
 }
 
 var (
 	flag        = &Cmd.Flag
 	flagNotExec = flag.Bool("n", false, "prints commands that would be executed.")
+	flagMoveGo  = flag.Bool("mvgo", false, "move .go files to .gop files (only available in `--smart` mode).")
+	flagSmart   = flag.Bool("smart", false, "convert Go code style into Go+ style.")
 )
 
 func init() {
@@ -59,12 +63,17 @@ var (
 	rootDir = ""
 )
 
-func gopfmt(path string) (err error) {
+func gopfmt(path string, smart, mvgo bool) (err error) {
 	src, err := ioutil.ReadFile(path)
 	if err != nil {
 		return
 	}
-	target, err := format.Source(src)
+	var target []byte
+	if smart {
+		target, err = xformat.Source(src)
+	} else {
+		target, err = format.Source(src)
+	}
 	if err != nil {
 		return
 	}
@@ -72,6 +81,13 @@ func gopfmt(path string) (err error) {
 		return
 	}
 	fmt.Println(path)
+	if mvgo {
+		newPath := strings.TrimSuffix(path, ".go") + ".gop"
+		if err = os.WriteFile(newPath, target, 0666); err != nil {
+			return
+		}
+		return os.Remove(path)
+	}
 	return writeFileWithBackup(path, target)
 }
 
@@ -108,7 +124,9 @@ func walk(path string, d fs.DirEntry, err error) error {
 			if *flagNotExec {
 				fmt.Println("gop fmt", path)
 			} else {
-				err = gopfmt(path)
+				smart := *flagSmart
+				mvgo := smart && *flagMoveGo
+				err = gopfmt(path, smart && (mvgo || ext != ".go"), mvgo)
 			}
 		}
 	}
diff --git a/x/format/gopstyle.go b/x/format/gopstyle.go
index 1091ad80..68d8cf47 100644
--- a/x/format/gopstyle.go
+++ b/x/format/gopstyle.go
@@ -1,18 +1,37 @@
 package format
 
 import (
+	"bytes"
+
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/format"
+	"github.com/goplus/gop/parser"
+	"github.com/goplus/gop/token"
 )
 
 // -----------------------------------------------------------------------------
 
+func Source(src []byte) (ret []byte, err error) {
+	fset := token.NewFileSet()
+	if f, err := parser.ParseFile(fset, "", src, parser.ParseComments); err == nil {
+		Gopstyle(f)
+		var buf bytes.Buffer
+		if err = format.Node(&buf, fset, f); err == nil {
+			ret = buf.Bytes()
+		}
+	}
+	return
+}
+
+// -----------------------------------------------------------------------------
+
 func Gopstyle(file *ast.File) {
 	if identEqual(file.Name, "main") {
 		file.NoPkgDecl = true
 	}
 	if idx := findFuncDecl(file.Decls, "main"); idx >= 0 {
 		last := len(file.Decls) - 1
-		if idx != last {
+		if idx != last { // swap main func to last
 			fn := file.Decls[idx]
 			copy(file.Decls[idx:], file.Decls[idx+1:])
 			file.Decls[last] = fn


Test patch:
diff --git a/x/format/gopstyle_test.go b/x/format/gopstyle_test.go
index 29de6a70..f9f9d7df 100644
--- a/x/format/gopstyle_test.go
+++ b/x/format/gopstyle_test.go
@@ -1,30 +1,18 @@
 package format
 
 import (
-	"bytes"
 	"testing"
-
-	"github.com/goplus/gop/format"
-	"github.com/goplus/gop/parser"
-	"github.com/goplus/gop/token"
 )
 
 // -----------------------------------------------------------------------------
 
 func testFormat(t *testing.T, name string, src, expect string) {
 	t.Run(name, func(t *testing.T) {
-		fset := token.NewFileSet()
-		f, err := parser.ParseFile(fset, "foo.gop", src, parser.ParseComments)
-		if err != nil {
-			t.Fatal("parser.ParseFile failed:", err)
-		}
-		Gopstyle(f)
-		var buf bytes.Buffer
-		err = format.Node(&buf, fset, f)
+		result, err := Source([]byte(src))
 		if err != nil {
-			t.Fatal("format.Node failed:", err)
+			t.Fatal("format.Source failed:", err)
 		}
-		if ret := buf.String(); ret != expect {
+		if ret := string(result); ret != expect {
 			t.Fatalf("%s => Expect:\n%s\n=> Got:\n%s\n", name, expect, ret)
 		}
 	})