Repository: goplus/gop
Pull request #1524 (2023-11-07 11:48:04)

gop/x/langserver


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 7c6b5f1c..250fe833 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -139,17 +139,18 @@ func getStringConst(spx *gox.PkgRef, name string) string {
 	return ""
 }
 
-func getFields(f *ast.File) []ast.Spec {
+func getFields(f *ast.File) (specs []ast.Spec) {
 	for _, decl := range f.Decls {
 		if g, ok := decl.(*ast.GenDecl); ok {
 			if g.Tok == token.VAR {
-				return g.Specs
+				specs, g.Specs = g.Specs, nil
+				return
 			}
 			continue
 		}
 		break
 	}
-	return nil
+	return
 }
 
 func setBodyHandler(ctx *blockCtx) {
diff --git a/cl/compile.go b/cl/compile.go
index 25a34f27..67d4da0f 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -758,34 +758,21 @@ func preloadGopFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, con
 						tags = append(tags, "")
 					}
 				}
-				rec := ctx.recorder()
 				for _, v := range specs {
 					spec := v.(*ast.ValueSpec)
+					var embed bool
+					if spec.Names == nil {
+						embed = true
+						v := parseTypeEmbedName(spec.Type)
+						spec.Names = []*ast.Ident{v}
+					}
 					typ := toType(ctx, spec.Type)
-					tag := toFieldTag(spec.Tag)
-					if len(spec.Names) == 0 {
-						name := parseTypeEmbedName(spec.Type)
-						if chk.chkRedecl(ctx, name.Name, spec.Type.Pos()) {
+					for _, name := range spec.Names {
+						if chk.chkRedecl(ctx, name.Name, name.Pos()) {
 							continue
 						}
-						fld := types.NewField(spec.Type.Pos(), pkg, name.Name, typ, true)
-						if rec != nil {
-							rec.Def(name, fld)
-						}
-						flds = append(flds, fld)
-						tags = append(tags, tag)
-					} else {
-						for _, name := range spec.Names {
-							if chk.chkRedecl(ctx, name.Name, name.Pos()) {
-								continue
-							}
-							fld := types.NewField(name.Pos(), pkg, name.Name, typ, false)
-							if rec != nil {
-								rec.Def(name, fld)
-							}
-							flds = append(flds, fld)
-							tags = append(tags, tag)
-						}
+						flds = append(flds, types.NewField(name.Pos(), pkg, name.Name, typ, embed))
+						tags = append(tags, toFieldTag(spec.Tag))
 					}
 				}
 				decl.InitType(p, types.NewStruct(flds, tags))
@@ -847,10 +834,6 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, gopFil
 	goFile := genGoFile(file, gopFile)
 	old, _ := p.SetCurFile(goFile, true)
 	defer p.RestoreCurFile(old)
-	var skipClassFields bool
-	if f.IsClass {
-		skipClassFields = true
-	}
 	for _, decl := range f.Decls {
 		switch d := decl.(type) {
 		case *ast.FuncDecl:
@@ -987,10 +970,6 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, gopFil
 					})
 				}
 			case token.VAR:
-				if skipClassFields {
-					skipClassFields = false
-					continue
-				}
 				for _, spec := range d.Specs {
 					vSpec := spec.(*ast.ValueSpec)
 					if debugLoad {
diff --git a/x/langserver/client.go b/x/langserver/client.go
index 37db9e1c..fb4cdb9a 100644
--- a/x/langserver/client.go
+++ b/x/langserver/client.go
@@ -42,9 +42,9 @@ type Client struct {
 	conn *jsonrpc2.Connection
 }
 
-// Dial uses the dialer to make a new connection and returns a client of the language server
+// Open uses the dialer to make a new connection and returns a client of the LangServer
 // based on the connection.
-func Dial(ctx context.Context, dialer Dialer) (ret Client, err error) {
+func Open(ctx context.Context, dialer Dialer) (ret Client, err error) {
 	c, err := jsonrpc2.Dial(ctx, dialer, jsonrpc2.BinderFunc(
 		func(ctx context.Context, c *jsonrpc2.Connection) (ret jsonrpc2.ConnectionOptions) {
 			return
@@ -56,6 +56,10 @@ func Dial(ctx context.Context, dialer Dialer) (ret Client, err error) {
 	return
 }
 
+func (p Client) Close() error {
+	return p.conn.Close()
+}
+
 func (p Client) AsyncGenGo(ctx context.Context, pattern ...string) *AsyncCall {
 	return p.conn.Call(ctx, methodGenGo, pattern)
 }
diff --git a/x/langserver/serve_dial.go b/x/langserver/serve_dial.go
new file mode 100644
index 00000000..200d467a
--- /dev/null
+++ b/x/langserver/serve_dial.go
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed 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 langserver
+
+import (
+	"context"
+	"io"
+	"log"
+	"os"
+	"os/exec"
+
+	"github.com/goplus/gop/x/jsonrpc2/stdio"
+)
+
+func fatal(err error) {
+	log.Fatalln(err)
+}
+
+// -----------------------------------------------------------------------------
+
+// ServeAndDialConfig represents the configuration of ServeAndDial.
+type ServeAndDialConfig struct {
+	// LogFile is where the LangServer application log saves to (optional).
+	// Default is ~/.gop/serve.log
+	LogFile string
+
+	// OnError is to customize how to process errors (optional).
+	// It should panic in any case.
+	OnError func(err error)
+}
+
+// ServeAndDial executes a command as a LangServer, makes a new connection to it
+// and returns a client of the LangServer based on the connection.
+func ServeAndDial(cmd string, args []string, conf *ServeAndDialConfig) Client {
+	if conf == nil {
+		conf = new(ServeAndDialConfig)
+	}
+	onErr := conf.OnError
+	if onErr == nil {
+		onErr = fatal
+	}
+	logFile := conf.LogFile
+	if logFile == "" {
+		home, err := os.UserHomeDir()
+		if err != nil {
+			onErr(err)
+		}
+		gopDir := home + "/.gop"
+		err = os.MkdirAll(gopDir, 0755)
+		if err != nil {
+			onErr(err)
+		}
+		logFile = gopDir + "/serve.log"
+	}
+
+	in, w := io.Pipe()
+	r, out := io.Pipe()
+
+	go func() {
+		defer r.Close()
+		defer w.Close()
+
+		f, err := os.Create(logFile)
+		if err != nil {
+			onErr(err)
+		}
+		defer f.Close()
+
+		cmd := exec.Command(cmd, args...)
+		cmd.Stdin = r
+		cmd.Stdout = w
+		cmd.Stderr = f
+		cmd.Run()
+	}()
+
+	ctx := context.Background()
+	c, err := Open(ctx, stdio.Dialer(in, out))
+	if err != nil {
+		onErr(err)
+	}
+	return c
+}
+
+// -----------------------------------------------------------------------------
diff --git a/x/langserver/langserver.go b/x/langserver/server.go
similarity index 98%
rename from x/langserver/langserver.go
rename to x/langserver/server.go
index 551fee12..22bf1633 100644
--- a/x/langserver/langserver.go
+++ b/x/langserver/server.go
@@ -41,6 +41,7 @@ type Config struct {
 	Framer jsonrpc2.Framer
 }
 
+// NewServer creates a new LangServer and returns it.
 func NewServer(ctx context.Context, listener Listener, conf *Config) (ret *Server) {
 	h := new(handler)
 	ret = jsonrpc2.NewServer(ctx, listener, jsonrpc2.BinderFunc(
diff --git a/x/typesutil/check.go b/x/typesutil/check.go
index ae5947fd..92ee7ddd 100644
--- a/x/typesutil/check.go
+++ b/x/typesutil/check.go
@@ -164,6 +164,7 @@ func (p *Checker) Files(goFiles []*goast.File, gopFiles []*ast.File) (err error)
 			log.Println("typesutil.Check err:", err)
 			log.SingleStack()
 		}
+		return
 	}
 	if len(files) > 0 {
 		scope := pkgTypes.Scope()


Test patch:
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 9d387a78..28e0fd71 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -303,15 +303,15 @@ func onCloned() {
 
 import "github.com/goplus/gop/cl/internal/spx"
 
+type Game struct {
+	*spx.MyGame
+	Kai Kai
+}
 type Kai struct {
 	spx.Sprite
 	*Game
 	a int
 }
-type Game struct {
-	*spx.MyGame
-	Kai Kai
-}
 
 func (this *Game) onInit() {
 	spx.Gopt_Sprite_Clone__0(this.Kai)
@@ -345,11 +345,6 @@ import (
 	"github.com/goplus/gop/cl/internal/spx"
 )
 
-type Kai struct {
-	spx.Sprite
-	*index
-}
-
 var x float64 = spx.Rand__1(1.2)
 
 type index struct {
@@ -357,6 +352,10 @@ type index struct {
 	Kai Kai
 	t   spx.Sound
 }
+type Kai struct {
+	spx.Sprite
+	*index
+}
 
 func (this *index) MainEntry() {
 	spx.Gopt_MyGame_Run(this, "hzip://open.qiniu.us/weather/res.zip")
@@ -494,14 +493,14 @@ var (
 
 import "github.com/goplus/gop/cl/internal/spx2"
 
-type Kai struct {
-	spx2.Sprite
-	*Game
-}
 type Game struct {
 	spx2.Game
 	Kai Kai
 }
+type Kai struct {
+	spx2.Sprite
+	*Game
+}
 
 func (this *Game) MainEntry() {
 }
@@ -530,14 +529,14 @@ import (
 	"github.com/goplus/gop/cl/internal/spx2"
 )
 
-type Kai struct {
-	spx2.Sprite
-	*Game
-}
 type Game struct {
 	spx2.Game
 	Kai Kai
 }
+type Kai struct {
+	spx2.Sprite
+	*Game
+}
 
 func (this *Game) MainEntry() {
 	fmt.Println("Hi")
@@ -631,15 +630,15 @@ type info struct {
 	x int
 	y int
 }
+type Game struct {
+	*spx.MyGame
+	Kai Kai
+}
 type Kai struct {
 	spx.Sprite
 	*Game
 	a int
 }
-type Game struct {
-	*spx.MyGame
-	Kai Kai
-}
 
 func (this *Game) onInit() {
 	spx.Gopt_Sprite_Clone__0(this.Kai)