From 6863a5bf589c7ed681d33b587f4c225d04a2d534 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 7 Nov 2020 07:03:04 +1100 Subject: [PATCH] Reworked project parser *working* --- v2/pkg/parser/applicationVariable.go | 55 ++++++ v2/pkg/parser/boundStructs.go | 155 ++++++++++++++++ v2/pkg/parser/comments.go | 21 +++ v2/pkg/parser/field.go | 112 ++++++++++++ v2/pkg/parser/functions.go | 5 + v2/pkg/parser/method.go | 106 +++++++++++ v2/pkg/parser/parseStruct.go | 93 ++++++++++ v2/pkg/parser/parser.go | 168 ++++++++++++++++++ v2/pkg/parser/parser_test.go | 46 +++++ v2/pkg/parser/struct.go | 109 ++++++++++++ v2/pkg/parser/testproject/basic.go | 59 ++++++ v2/pkg/parser/testproject/go.mod | 9 + v2/pkg/parser/testproject/go.sum | 83 +++++++++ v2/pkg/parser/testproject/main.go | 21 +++ .../parser/testproject/mypackage/mypackage.go | 36 ++++ 15 files changed, 1078 insertions(+) create mode 100644 v2/pkg/parser/applicationVariable.go create mode 100644 v2/pkg/parser/boundStructs.go create mode 100644 v2/pkg/parser/comments.go create mode 100644 v2/pkg/parser/field.go create mode 100644 v2/pkg/parser/functions.go create mode 100644 v2/pkg/parser/method.go create mode 100644 v2/pkg/parser/parseStruct.go create mode 100644 v2/pkg/parser/parser.go create mode 100644 v2/pkg/parser/parser_test.go create mode 100644 v2/pkg/parser/struct.go create mode 100644 v2/pkg/parser/testproject/basic.go create mode 100644 v2/pkg/parser/testproject/go.mod create mode 100644 v2/pkg/parser/testproject/go.sum create mode 100644 v2/pkg/parser/testproject/main.go create mode 100644 v2/pkg/parser/testproject/mypackage/mypackage.go diff --git a/v2/pkg/parser/applicationVariable.go b/v2/pkg/parser/applicationVariable.go new file mode 100644 index 00000000..ec8400ff --- /dev/null +++ b/v2/pkg/parser/applicationVariable.go @@ -0,0 +1,55 @@ +package parser + +import ( + "go/ast" + + "golang.org/x/tools/go/packages" +) + +func (p *Parser) getApplicationVariableName(pkg *packages.Package, wailsImportName string) (string, bool) { + + var applicationVariableName = "" + + // Iterate through the whole package looking for the application name + for _, fileAst := range pkg.Syntax { + ast.Inspect(fileAst, func(n ast.Node) bool { + // Parse Assignments looking for application name + if assignStmt, ok := n.(*ast.AssignStmt); ok { + + // Check the RHS is of the form: + // `app := wails.CreateApp()` or + // `app := wails.CreateAppWithOptions` + for _, rhs := range assignStmt.Rhs { + ce, ok := rhs.(*ast.CallExpr) + if !ok { + continue + } + se, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + i, ok := se.X.(*ast.Ident) + if !ok { + continue + } + // Have we found the wails import name? + if i.Name == wailsImportName { + // Check we are calling a function to create the app + if se.Sel.Name == "CreateApp" || se.Sel.Name == "CreateAppWithOptions" { + if len(assignStmt.Lhs) == 1 { + i, ok := assignStmt.Lhs[0].(*ast.Ident) + if ok { + // Found the app variable name + applicationVariableName = i.Name + return false + } + } + } + } + } + } + return true + }) + } + return applicationVariableName, applicationVariableName != "" +} diff --git a/v2/pkg/parser/boundStructs.go b/v2/pkg/parser/boundStructs.go new file mode 100644 index 00000000..13f004b0 --- /dev/null +++ b/v2/pkg/parser/boundStructs.go @@ -0,0 +1,155 @@ +package parser + +import ( + "go/ast" + + "golang.org/x/tools/go/packages" +) + +func (p *Parser) getImportByName(pkg *packages.Package, importName string) *packages.Package { + // Find package path + for _, imp := range pkg.Imports { + if imp.Name == importName { + return imp + } + } + return nil +} + +func (p *Parser) findBoundStructsInPackage(pkg *packages.Package, applicationVariableName string) []*StructReference { + + var boundStructs []*StructReference + + // Iterate through the whole package looking for the bound structs + for _, fileAst := range pkg.Syntax { + ast.Inspect(fileAst, func(n ast.Node) bool { + // Parse Call expressions looking for bind calls + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + // Check this is the right kind of expression (something.something()) + f, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + ident, ok := f.X.(*ast.Ident) + if !ok { + return true + } + + if ident.Name != applicationVariableName { + return true + } + + if f.Sel.Name != "Bind" { + return true + } + + if len(callExpr.Args) != 1 { + return true + } + + // Work out what was bound + switch boundItem := callExpr.Args[0].(type) { + + // // app.Bind( someFunction() ) + // case *ast.CallExpr: + // switch fn := boundItem.Fun.(type) { + // case *ast.Ident: + // boundStructs = append(boundStructs, newStruct(pkg.Name, fn.Name)) + // println("Found bound function:", fn.Name) + // case *ast.SelectorExpr: + // ident, ok := fn.X.(*ast.Ident) + // if !ok { + // return true + // } + // packageName := ident.Name + // functionName := fn.Sel.Name + // println("Found bound function:", packageName+"."+functionName) + + // strct := p.getFunctionReturnType(packageName, functionName) + // if strct == nil { + // // Unable to resolve function + // return true + // } + // boundStructs = append(boundStructs, strct) + // } + + // Binding struct pointer literals + case *ast.UnaryExpr: + + if boundItem.Op.String() != "&" { + return true + } + + cl, ok := boundItem.X.(*ast.CompositeLit) + if !ok { + return true + } + + switch boundStructExp := cl.Type.(type) { + + // app.Bind( &myStruct{} ) + case *ast.Ident: + boundStruct := newStructReference(pkg.Name, boundStructExp.Name) + boundStructs = append(boundStructs, boundStruct) + + // app.Bind( &mypackage.myStruct{} ) + case *ast.SelectorExpr: + var structName = "" + var packageName = "" + switch x := boundStructExp.X.(type) { + case *ast.Ident: + packageName = x.Name + default: + // TODO: Save these warnings + // println("Identifier in binding not supported:") + return true + } + structName = boundStructExp.Sel.Name + referencedPackage := p.getImportByName(pkg, packageName) + boundStruct := newStructReference(referencedPackage.Name, structName) + boundStructs = append(boundStructs, boundStruct) + } + + // Binding struct literals + case *ast.CompositeLit: + switch literal := boundItem.Type.(type) { + + // app.Bind( myStruct{} ) + case *ast.Ident: + structName := literal.Name + boundStruct := newStructReference(pkg.Name, structName) + boundStructs = append(boundStructs, boundStruct) + + // app.Bind( mypackage.myStruct{} ) + case *ast.SelectorExpr: + var structName = "" + var packageName = "" + switch x := literal.X.(type) { + case *ast.Ident: + packageName = x.Name + default: + // TODO: Save these warnings + // println("Identifier in binding not supported:") + return true + } + structName = literal.Sel.Name + + referencedPackage := p.getImportByName(pkg, packageName) + boundStruct := newStructReference(referencedPackage.Name, structName) + boundStructs = append(boundStructs, boundStruct) + } + + default: + // TODO: Save these warnings + // println("Unsupported bind expression:") + // spew.Dump(boundItem) + } + + return true + }) + } + return boundStructs +} diff --git a/v2/pkg/parser/comments.go b/v2/pkg/parser/comments.go new file mode 100644 index 00000000..0f1a32d1 --- /dev/null +++ b/v2/pkg/parser/comments.go @@ -0,0 +1,21 @@ +package parser + +import ( + "go/ast" + "strings" +) + +func (p *Parser) parseComments(comments *ast.CommentGroup) []string { + var result []string + + if comments == nil { + return result + } + + for _, comment := range comments.List { + commentText := strings.TrimPrefix(comment.Text, "//") + result = append(result, commentText) + } + + return result +} diff --git a/v2/pkg/parser/field.go b/v2/pkg/parser/field.go new file mode 100644 index 00000000..7fc2c7c5 --- /dev/null +++ b/v2/pkg/parser/field.go @@ -0,0 +1,112 @@ +package parser + +import ( + "fmt" + "go/ast" +) + +// Field defines a parsed struct field +type Field struct { + Name string + Type string + Struct *Struct + Comments []string + + // This struct reference is to temporarily hold the name + // of the struct during parsing + structReference *StructReference +} + +func (p *Parser) parseField(field *ast.Field, thisPackageName string) ([]*Field, error) { + var result []*Field + + var fieldType string + var structReference *StructReference + + // Determine type + switch t := field.Type.(type) { + case *ast.Ident: + fieldType = t.Name + case *ast.StarExpr: + fieldType = "struct" + packageName, structName, err := p.parseStructNameFromStarExpr(t) + if err != nil { + return nil, err + } + + // If we don't ahve a package name, it means it's in this package + if packageName == "" { + packageName = thisPackageName + } + + // Temporarily store the struct reference + structReference = newStructReference(packageName, structName) + + default: + return nil, fmt.Errorf("Unsupported field found in struct: %+v", t) + } + + // Loop over names if we have + if len(field.Names) > 0 { + for _, name := range field.Names { + + // Create a field per name + thisField := &Field{ + Comments: p.parseComments(field.Doc), + } + thisField.Name = name.Name + thisField.Type = fieldType + thisField.structReference = structReference + + result = append(result, thisField) + } + return result, nil + } + + // When we have no name + thisField := &Field{ + Comments: p.parseComments(field.Doc), + } + thisField.Type = fieldType + thisField.structReference = structReference + + result = append(result, thisField) + + return result, nil +} + +func (p *Parser) resolveFieldReferences(fields []*Field) error { + + // Loop over fields + for _, field := range fields { + + // If we have a struct reference but no actual struct, + // we need to resolve it + if field.structReference != nil && field.Struct == nil { + fqn := field.structReference.FullyQualifiedName() + println("Need to resolve struct reference: ", fqn) + // Check the cache for the struct + structPointer, err := p.ParseStruct(field.structReference.Package, field.structReference.Name) + if err != nil { + return err + } + field.Struct = structPointer + if field.Struct != nil { + // Save the fact that the struct is used as data + field.Struct.UsedAsData = true + println("Resolved struct reference:", fqn) + + // Resolve *its* references + err = p.resolveStructReferences(field.Struct) + if err != nil { + return err + } + } else { + println("Unable to resolve struct reference:", fqn) + } + + } + } + + return nil +} diff --git a/v2/pkg/parser/functions.go b/v2/pkg/parser/functions.go new file mode 100644 index 00000000..69e05510 --- /dev/null +++ b/v2/pkg/parser/functions.go @@ -0,0 +1,5 @@ +package parser + +func (p *Parser) getFunctionReturnType(packageName string, functionName string) *Struct { + return nil +} diff --git a/v2/pkg/parser/method.go b/v2/pkg/parser/method.go new file mode 100644 index 00000000..83d5cc9d --- /dev/null +++ b/v2/pkg/parser/method.go @@ -0,0 +1,106 @@ +package parser + +import ( + "go/ast" + "strings" +) + +// Method defines a struct method +type Method struct { + Name string + Comments []string + Inputs []*Field + Returns []*Field +} + +func (p *Parser) parseStructMethods(boundStruct *Struct) error { + for _, fileAst := range boundStruct.Package.Syntax { + + // Track errors + var parseError error + + ast.Inspect(fileAst, func(n ast.Node) bool { + + if funcDecl, ok := n.(*ast.FuncDecl); ok { + + if funcDecl.Recv == nil { + return true + } + + // This is a struct method + for _, field := range funcDecl.Recv.List { + switch f := field.Type.(type) { + case *ast.StarExpr: + // This is a struct pointer method + ident, ok := f.X.(*ast.Ident) // _ ? + if !ok { + continue + } + + // Check this method is for this struct + if ident.Name != boundStruct.Name { + continue + } + + // We want to ignore Internal functions + if p.internalMethods.Contains(funcDecl.Name.Name) { + continue + } + + // If this method is not Public, ignore + if string(funcDecl.Name.Name[0]) != strings.ToUpper((string(funcDecl.Name.Name[0]))) { + continue + } + + // Create our struct + structMethod := &Method{ + Name: funcDecl.Name.Name, + Comments: p.parseComments(funcDecl.Doc), + } + + // Save the input parameters + if funcDecl.Type.Params != nil { + for _, inputField := range funcDecl.Type.Params.List { + fields, err := p.parseField(inputField, boundStruct.Package.Name) + if err != nil { + parseError = err + return false + } + + structMethod.Inputs = append(structMethod.Inputs, fields...) + } + } + + // Save the output parameters + if funcDecl.Type.Results != nil { + for _, outputField := range funcDecl.Type.Results.List { + fields, err := p.parseField(outputField, boundStruct.Package.Name) + if err != nil { + parseError = err + return false + } + + structMethod.Returns = append(structMethod.Returns, fields...) + } + } + + // Append this method to the parsed struct + boundStruct.Methods = append(boundStruct.Methods, structMethod) + + default: + // Unsupported + continue + } + } + } + return true + }) + + // If we got an error, return it + if parseError != nil { + return parseError + } + } + + return nil +} diff --git a/v2/pkg/parser/parseStruct.go b/v2/pkg/parser/parseStruct.go new file mode 100644 index 00000000..6d88800b --- /dev/null +++ b/v2/pkg/parser/parseStruct.go @@ -0,0 +1,93 @@ +package parser + +import ( + "go/ast" +) + +// getCachedStruct attempts to get an already parsed struct from the +// struct cache +func (p *Parser) getCachedStruct(packageName string, structName string) *Struct { + fqn := packageName + "." + structName + return p.parsedStructs[fqn] +} + +// ParseStruct will attempt to parse the given struct using +// the package it references +func (p *Parser) ParseStruct(packageName string, structName string) (*Struct, error) { + + // Check the cache + result := p.getCachedStruct(packageName, structName) + if result != nil { + return result, nil + } + + // Find the package + pkg := p.getPackageByName(packageName) + if pkg == nil { + // TODO: Find package via imports? + println("Cannot find package", packageName) + return nil, nil + } + + // Iterate through the whole package looking for the bound structs + for _, fileAst := range pkg.Syntax { + + // Track errors + var parseError error + + ast.Inspect(fileAst, func(n ast.Node) bool { + if genDecl, ok := n.(*ast.GenDecl); ok { + for _, spec := range genDecl.Specs { + if typeSpec, ok := spec.(*ast.TypeSpec); ok { + if structType, ok := typeSpec.Type.(*ast.StructType); ok { + structDefinitionName := typeSpec.Name.Name + if structDefinitionName == structName { + + // Create the new struct + result = p.newStruct(pkg, structDefinitionName) + + // Save comments + result.Comments = p.parseComments(genDecl.Doc) + + parseError = p.parseStructMethods(result) + if parseError != nil { + return false + } + + // Parse the struct fields + parseError = p.parseStructFields(structType, result) + + // Cache this struct + key := result.FullyQualifiedName() + p.parsedStructs[key] = result + + return false + } + } + } + } + } + return true + }) + + // If we got an error, return it + if parseError != nil { + return nil, parseError + } + } + return result, nil +} + +func (p *Parser) parseStructFields(structType *ast.StructType, boundStruct *Struct) error { + + // Parse the fields + for _, field := range structType.Fields.List { + fields, err := p.parseField(field, boundStruct.Package.Name) + if err != nil { + return err + } + boundStruct.Fields = append(boundStruct.Fields, fields...) + } + + return nil +} diff --git a/v2/pkg/parser/parser.go b/v2/pkg/parser/parser.go new file mode 100644 index 00000000..d1ea9529 --- /dev/null +++ b/v2/pkg/parser/parser.go @@ -0,0 +1,168 @@ +package parser + +import ( + "fmt" + "go/token" + + "github.com/davecgh/go-spew/spew" + "github.com/leaanthony/slicer" + "github.com/pkg/errors" + "golang.org/x/tools/go/packages" +) + +type Parser struct { + + // Placeholders for Go's parser + goPackages []*packages.Package + fileSet *token.FileSet + internalMethods *slicer.StringSlicer + + // This is a map of structs that have been parsed + // The key is . + parsedStructs map[string]*Struct + + // The list of struct names that are bound + BoundStructReferences []*StructReference + + // The list of structs that are bound + BoundStructs []*Struct +} + +func NewParser() *Parser { + return &Parser{ + fileSet: token.NewFileSet(), + internalMethods: slicer.String([]string{"WailsInit", "WailsShutdown"}), + parsedStructs: make(map[string]*Struct), + } +} + +// ParseProject will parse the Wails project in the given directory +func (p *Parser) ParseProject(dir string) error { + + var err error + + err = p.loadPackages(dir) + if err != nil { + return err + } + + err = p.findBoundStructs() + if err != nil { + return err + } + + err = p.parseBoundStructs() + if err != nil { + return err + } + + spew.Dump(p.BoundStructs) + println("******* Parsed Structs *******") + fmt.Printf("%+v\n", p.parsedStructs) + + return err +} + +func (p *Parser) loadPackages(projectPath string) error { + mode := packages.NeedName | + packages.NeedFiles | + packages.NeedSyntax | + packages.NeedTypes | + packages.NeedImports | + packages.NeedTypesInfo + + cfg := &packages.Config{Fset: p.fileSet, Mode: mode, Dir: projectPath} + pkgs, err := packages.Load(cfg, "./...") + if err != nil { + return errors.Wrap(err, "Problem loading packages") + } + // Check for errors + var parseError error + for _, pkg := range pkgs { + for _, err := range pkg.Errors { + if parseError == nil { + parseError = errors.New(err.Error()) + } else { + parseError = errors.Wrap(parseError, err.Error()) + } + } + } + + if parseError != nil { + return parseError + } + + p.goPackages = pkgs + + return nil +} + +func (p *Parser) getPackageByName(packageName string) *packages.Package { + for _, pkg := range p.goPackages { + if pkg.Name == packageName { + return pkg + } + } + return nil +} + +func (p *Parser) getWailsImportName(pkg *packages.Package) (string, bool) { + // Scan the imports for the wails v2 import + for key, details := range pkg.Imports { + if key == "github.com/wailsapp/wails/v2" { + return details.Name, true + } + } + return "", false +} + +// findBoundStructs will search through the Wails project looking +// for which structs have been bound using the `Bind()` method +func (p *Parser) findBoundStructs() error { + + // Try each of the packages to find the Bind() calls + for _, pkg := range p.goPackages { + + // Does this package import Wails? + wailsImportName, imported := p.getWailsImportName(pkg) + if !imported { + continue + } + + // Do we create an app using CreateApp? + appVariableName, created := p.getApplicationVariableName(pkg, wailsImportName) + if !created { + continue + } + + boundStructReferences := p.findBoundStructsInPackage(pkg, appVariableName) + p.BoundStructReferences = append(p.BoundStructReferences, boundStructReferences...) + } + + return nil +} + +func (p *Parser) parseBoundStructs() error { + + // Iterate the structs + for _, boundStructReference := range p.BoundStructReferences { + // Parse the struct + boundStruct, err := p.ParseStruct(boundStructReference.Package, boundStructReference.Name) + if err != nil { + return err + } + + p.BoundStructs = append(p.BoundStructs, boundStruct) + } + + // Resolve the references between the structs + // This is when a field of one struct is a struct type + for _, boundStruct := range p.BoundStructs { + err := p.resolveStructReferences(boundStruct) + if err != nil { + return err + } + } + + return nil +} diff --git a/v2/pkg/parser/parser_test.go b/v2/pkg/parser/parser_test.go new file mode 100644 index 00000000..6ab1d4fa --- /dev/null +++ b/v2/pkg/parser/parser_test.go @@ -0,0 +1,46 @@ +package parser + +import ( + "testing" + + "github.com/leaanthony/slicer" + "github.com/matryer/is" + "github.com/wailsapp/wails/v2/internal/fs" +) + +func TestParser(t *testing.T) { + + is := is.New(t) + + // Local project dir + projectDir := fs.RelativePath("./testproject") + + p := NewParser() + + // Check parsing worked + err := p.ParseProject(projectDir) + is.NoErr(err) + + // Expected structs + expectedBoundStructs := slicer.String() + expectedBoundStructs.Add("main.Basic", "mypackage.Manager") + + // We expect these to be the same length + is.Equal(expectedBoundStructs.Length(), len(p.BoundStructs)) + + // Check bound structs + for _, boundStruct := range p.BoundStructs { + + // Check the names are correct + fqn := boundStruct.FullyQualifiedName() + is.True(expectedBoundStructs.Contains(fqn)) + + // Check that the structs have comments + is.True(len(boundStruct.Comments) > 0) + + // Check that the structs have methods + is.True(len(boundStruct.Methods) > 0) + + } + +} diff --git a/v2/pkg/parser/struct.go b/v2/pkg/parser/struct.go new file mode 100644 index 00000000..c606bfc2 --- /dev/null +++ b/v2/pkg/parser/struct.go @@ -0,0 +1,109 @@ +package parser + +import ( + "fmt" + "go/ast" + + "golang.org/x/tools/go/packages" +) + +type Struct struct { + Package *packages.Package + Name string + Comments []string + Fields []*Field + Methods []*Method + + // This is true when this struct is used as a datatype + UsedAsData bool +} + +// newStruct creates a new struct and stores in the cache +func (p *Parser) newStruct(pkg *packages.Package, name string) *Struct { + + result := &Struct{ + Package: pkg, + Name: name, + } + + return result +} + +// FullyQualifiedName returns the fully qualified name of this struct +func (s *Struct) FullyQualifiedName() string { + return s.Package.Name + "." + s.Name +} + +func (p *Parser) parseStructNameFromStarExpr(starExpr *ast.StarExpr) (string, string, error) { + pkg := "" + name := "" + // Determine the FQN + switch x := starExpr.X.(type) { + case *ast.SelectorExpr: + switch i := x.X.(type) { + case *ast.Ident: + pkg = i.Name + default: + return "", "", fmt.Errorf("Unsupported Selector expression: %+v", i) + } + + name = x.Sel.Name + + case *ast.StarExpr: + switch s := x.X.(type) { + case *ast.Ident: + name = s.Name + default: + return "", "", fmt.Errorf("Unsupported Star expression: %+v", s) + } + case *ast.Ident: + name = x.Name + default: + return "", "", fmt.Errorf("Unsupported Star.X expression: %+v", x) + } + return pkg, name, nil +} + +// StructReference defines a reference to a fully qualified struct +type StructReference struct { + Package string + Name string +} + +func newStructReference(packageName string, structName string) *StructReference { + return &StructReference{Package: packageName, Name: structName} +} + +// FullyQualifiedName returns a string representing the struct reference +func (s *StructReference) FullyQualifiedName() string { + return s.Package + "." + s.Name +} + +func (p *Parser) resolveStructReferences(boundStruct *Struct) error { + + var err error + + // Resolve field references + err = p.resolveFieldReferences(boundStruct.Fields) + if err != nil { + return nil + } + + // Check if method fields need resolving + for _, method := range boundStruct.Methods { + + // Resolve method inputs + err = p.resolveFieldReferences(method.Inputs) + if err != nil { + return nil + } + + // Resolve method outputs + err = p.resolveFieldReferences(method.Returns) + if err != nil { + return nil + } + } + + return nil +} diff --git a/v2/pkg/parser/testproject/basic.go b/v2/pkg/parser/testproject/basic.go new file mode 100644 index 00000000..374ec9bc --- /dev/null +++ b/v2/pkg/parser/testproject/basic.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + + wails "github.com/wailsapp/wails/v2" +) + +// Basic application struct +type Basic struct { + runtime *wails.Runtime +} + +// // Another application struct +// type Another struct { +// runtime *wails.Runtime +// } + +// func (a *Another) Doit() { + +// } + +// // newBasicPointer creates a new Basic application struct +// func newBasicPointer() *Basic { +// return &Basic{} +// } + +// // newBasic creates a new Basic application struct +// func newBasic() Basic { +// return Basic{} +// } + +// WailsInit is called at application startup +func (b *Basic) WailsInit(runtime *wails.Runtime) error { + // Perform your setup here + b.runtime = runtime + runtime.Window.SetTitle("jsbundle") + return nil +} + +// WailsShutdown is called at application termination +func (b *Basic) WailsShutdown() { + // Perform your teardown here +} + +// // NewPerson creates a new person +// func (b *Basic) NewPerson(name string, age int) *mypackage.Person { +// return &mypackage.Person{Name: name, Age: age} +// } + +// Greet returns a greeting for the given name +func (b *Basic) Greet(name string) string { + return fmt.Sprintf("Hello %s!", name) +} + +// // RemovePerson Removes the given person +// func (b *Basic) RemovePerson(p *mypackage.Person) { +// // dummy +// } diff --git a/v2/pkg/parser/testproject/go.mod b/v2/pkg/parser/testproject/go.mod new file mode 100644 index 00000000..21f9e0d7 --- /dev/null +++ b/v2/pkg/parser/testproject/go.mod @@ -0,0 +1,9 @@ +module testproject + +go 1.13 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-alpha +) + +replace github.com/wailsapp/wails/v2 v2.0.0-alpha => /home/lea/Data/projects/wails/v2 diff --git a/v2/pkg/parser/testproject/go.sum b/v2/pkg/parser/testproject/go.sum new file mode 100644 index 00000000..dae10cce --- /dev/null +++ b/v2/pkg/parser/testproject/go.sum @@ -0,0 +1,83 @@ +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/minify/v2 v2.9.5/go.mod h1:jshtBj/uUJH6JX1fuxTLnnHOA1RVJhF5MM+leJzDKb4= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= +github.com/tdewolff/parse/v2 v2.5.3/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/v2/pkg/parser/testproject/main.go b/v2/pkg/parser/testproject/main.go new file mode 100644 index 00000000..0968d911 --- /dev/null +++ b/v2/pkg/parser/testproject/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "testproject/mypackage" + + "github.com/wailsapp/wails/v2" +) + +func main() { + // Create application with options + app := wails.CreateApp("jsbundle", 1024, 768) + + /***** Struct Literal *****/ + + // Local struct pointer literal *WORKING* + app.Bind(&Basic{}) + + // External struct pointer literal + app.Bind(&mypackage.Manager{}) + +} diff --git a/v2/pkg/parser/testproject/mypackage/mypackage.go b/v2/pkg/parser/testproject/mypackage/mypackage.go new file mode 100644 index 00000000..5dcac83e --- /dev/null +++ b/v2/pkg/parser/testproject/mypackage/mypackage.go @@ -0,0 +1,36 @@ +// Package mypackage does all the things a mypackage can do +package mypackage + +type Address struct { + Number int + Street string + Town string + Postcode string +} + +// Person defines a Person in the application +type Person struct { + // Name is a name + Name string + Age int + Address *Address +} + +// Manager is the Mr Manager +type Manager struct { + Name string + TwoIC *Person +} + +// Hire me some peoples! +func (m *Manager) Hire(name, test string, bob int) *Person { + return &Person{Name: name} +} + +// func NewManagerPointer() *Manager { +// return &Manager{} +// } + +// func NewManager() Manager { +// return Manager{} +// }