Repository: goplus/gop
Pull request #1071 (2021-12-26 03:14:57)

OpenProject => OpenFiles+OpenDir


Patch:
diff --git a/cmd/goprun/goprun.go b/cmd/goprun/goprun.go
index 9aedf1b5..f486d7fc 100644
--- a/cmd/goprun/goprun.go
+++ b/cmd/goprun/goprun.go
@@ -15,7 +15,7 @@ func main() {
 		return
 	}
 	ctx := gopmod.New("")
-	goProj, err := ctx.OpenProject(0, os.Args[1])
+	goProj, err := ctx.OpenFiles(0, os.Args[1])
 	if err != nil {
 		fmt.Fprint(os.Stderr, "OpenProject failed:", err)
 		return
diff --git a/cmd/internal/gopproj/proj.go b/cmd/internal/gopproj/proj.go
new file mode 100644
index 00000000..488a80c5
--- /dev/null
+++ b/cmd/internal/gopproj/proj.go
@@ -0,0 +1,86 @@
+package gopproj
+
+import (
+	"errors"
+	"path/filepath"
+	"strings"
+	"syscall"
+)
+
+// -----------------------------------------------------------------------------
+
+type Proj = interface {
+	projObj()
+}
+
+type FilesProj struct {
+	Files []string
+}
+
+type PkgPathProj struct {
+	Path string
+}
+
+type DirProj struct {
+	Dir string
+}
+
+func (p *FilesProj) projObj()   {}
+func (p *PkgPathProj) projObj() {}
+func (p *DirProj) projObj()     {}
+
+// -----------------------------------------------------------------------------
+
+func ParseOne(args ...string) (proj Proj, next []string, err error) {
+	if len(args) == 0 {
+		return nil, nil, syscall.ENOENT
+	}
+	arg := args[0]
+	if isFile(arg) {
+		n := 1
+		for n < len(args) && isFile(args[n]) {
+			n++
+		}
+		return &FilesProj{Files: args[:n]}, args[n:], nil
+	}
+	if strings.HasPrefix(arg, ".") || strings.HasPrefix(arg, "/") {
+		return &DirProj{Dir: arg}, args[1:], nil
+	}
+	return &PkgPathProj{Path: arg}, args[1:], nil
+}
+
+func isFile(fname string) bool {
+	switch filepath.Ext(fname) {
+	case ".gop", ".go":
+		return true
+	}
+	return false
+}
+
+// -----------------------------------------------------------------------------
+
+func ParseAll(args ...string) (projs []Proj, err error) {
+	var hasFiles, hasNotFiles bool
+	for {
+		proj, next, e := ParseOne(args...)
+		if e != nil {
+			if hasFiles && hasNotFiles {
+				return nil, ErrMixedFilesProj
+			}
+			return
+		}
+		if _, ok := proj.(*FilesProj); ok {
+			hasFiles = true
+		} else {
+			hasNotFiles = true
+		}
+		projs = append(projs, proj)
+		args = next
+	}
+}
+
+var (
+	ErrMixedFilesProj = errors.New("mixed files project")
+)
+
+// -----------------------------------------------------------------------------
diff --git a/cmd/internal/run/mod.go b/cmd/internal/run/mod.go
index dcca5e28..15e27c79 100644
--- a/cmd/internal/run/mod.go
+++ b/cmd/internal/run/mod.go
@@ -33,7 +33,7 @@ func gopRun(source string, args ...string) {
 	if *flagGop {
 		flags = gopmod.FlagGoAsGoPlus
 	}
-	goProj, err := ctx.OpenProject(flags, source)
+	goProj, err := ctx.OpenFiles(flags, source)
 	if err != nil {
 		log.Fatalln("OpenProject failed:", err)
 	}
diff --git a/x/gopmod/gopproj.go b/x/gopmod/gopproj.go
index 5ca47e11..bbda0161 100644
--- a/x/gopmod/gopproj.go
+++ b/x/gopmod/gopproj.go
@@ -17,7 +17,6 @@
 package gopmod
 
 import (
-	"os"
 	"path/filepath"
 )
 
@@ -27,27 +26,18 @@ const (
 	FlagGoAsGoPlus = 1 << iota
 )
 
-func (p *Context) OpenProject(flags int, args ...string) (proj *Project, err error) {
+func (p *Context) OpenFiles(flags int, args ...string) (proj *Project, err error) {
 	if len(args) != 1 {
 		return p.openFromGopFiles(args)
 	}
 	src := args[0]
-	fi, err := os.Stat(src)
-	if err != nil {
-		return
-	}
-	if fi.IsDir() {
-		return p.openFromDir(src)
-	}
 	if (flags&FlagGoAsGoPlus) == 0 && filepath.Ext(src) == ".go" {
 		return openFromGoFile(src)
 	}
 	return p.openFromGopFiles(args)
 }
 
-// -----------------------------------------------------------------------------
-
-func (p *Context) openFromDir(dir string) (proj *Project, err error) {
+func (p *Context) OpenDir(flags int, dir string) (proj *Project, err error) {
 	panic("todo")
 }
 


Test patch:
diff --git a/cmd/internal/gopproj/proj_test.go b/cmd/internal/gopproj/proj_test.go
new file mode 100644
index 00000000..e3c34d69
--- /dev/null
+++ b/cmd/internal/gopproj/proj_test.go
@@ -0,0 +1,71 @@
+package gopproj
+
+import "testing"
+
+// -----------------------------------------------------------------------------
+
+func TestParseOne(t *testing.T) {
+	proj, next, err := ParseOne("a.go", "b.go", "abc")
+	if err != nil || len(next) != 1 || next[0] != "abc" {
+		t.Fatal("ParseOne failed:", proj, next, err)
+	}
+}
+
+func TestParseAll_wildcard1(t *testing.T) {
+	projs, err := ParseAll("*.go")
+	if err != nil || len(projs) != 1 {
+		t.Fatal("ParseAll failed:", projs, err)
+	}
+	if proj, ok := projs[0].(*FilesProj); !ok || len(proj.Files) != 1 || proj.Files[0] != "*.go" {
+		t.Fatal("ParseAll failed:", projs)
+	}
+}
+
+func TestParseAll_wildcard2(t *testing.T) {
+	projs, err := ParseAll("t/*.go")
+	if err != nil || len(projs) != 1 {
+		t.Fatal("ParseAll failed:", projs, err)
+	}
+	if proj, ok := projs[0].(*FilesProj); !ok || len(proj.Files) != 1 || proj.Files[0] != "t/*.go" {
+		t.Fatal("ParseAll failed:", projs)
+	}
+}
+
+func TestParseAll_multiFiles(t *testing.T) {
+	projs, err := ParseAll("a.gop", "b.go")
+	if err != nil || len(projs) != 1 {
+		t.Fatal("ParseAll failed:", projs, err)
+	}
+	if proj, ok := projs[0].(*FilesProj); !ok || len(proj.Files) != 2 || proj.Files[0] != "a.gop" {
+		t.Fatal("ParseAll failed:", proj)
+	}
+	projs[0].projObj()
+}
+
+func TestParseAll_multiProjs(t *testing.T) {
+	projs, err := ParseAll("a/...", "./a/...", "/a")
+	if err != nil || len(projs) != 3 {
+		t.Fatal("ParseAll failed:", projs, err)
+	}
+	if proj, ok := projs[0].(*PkgPathProj); !ok || proj.Path != "a/..." {
+		t.Fatal("ParseAll failed:", proj)
+	}
+	if proj, ok := projs[1].(*DirProj); !ok || proj.Dir != "./a/..." {
+		t.Fatal("ParseAll failed:", proj)
+	}
+	if proj, ok := projs[2].(*DirProj); !ok || proj.Dir != "/a" {
+		t.Fatal("ParseAll failed:", proj)
+	}
+	for _, proj := range projs {
+		proj.projObj()
+	}
+}
+
+func TestParseAllErr(t *testing.T) {
+	_, err := ParseAll("a/...", "./a/...", "/a", "*.go")
+	if err != ErrMixedFilesProj {
+		t.Fatal("ParseAll:", err)
+	}
+}
+
+// -----------------------------------------------------------------------------