Files
wails/v2/pkg/parser/field.go
Lea Anthony 71bfd29376 Feature/v2 mac (#555)
* Get it compiling

* Stubs in place to compile

* Semi runs

* add darwin platform for server

* Evaluation working correctly. Still WIP

* Ignore favicon for desktop

* lint

* Remove feature flag code

* More feature flag removal

* Support sending messages to the backend

* Callbacks working

* Add Center + refactor prefs

* Fix logger

* Callback hooks for MOAE

* Update packages

* ignore test builds

* Support Un/Fullscreen

* vscode stuff

* Only show window when rendered

* Get it compiling again!

* Support tons of stuff!

* Tidy up

* More refactoring

* WIP [bugged]

* Got get frame working

* fix setsize and setposition

* Add Mac Application Options

* Add HideTitleBar

* Support more mac window options

* Add Toolbar support for Mac

* Support colour

* Support runtime colour change

* Moved options to own package

* Refactored mac titlebar options

* Support hidden titlebar

* Support HiddenInset Titlebar

* Support TitleBar Default

Fixed merging defaults

* Sample titlebars

* Fix minmax app

* Min/Max size supported

* WIP: Support multiple value return

* Support OpenDialog

* Remove old dialog code

* change service bus topics for dialogs

* Revert changes to v1

* Use options struct for dialogs

* Initial support for OpenDialog

* Support selecting files+dirs

* Support multiple files in dialog

* Support all dialog properties

* Add comments

* Filter support

* Support default directory

* Support SaveDialog

* Tidy Up

* WIP: Basics of window drag

* Support window dragging

* Update tests

* Frameless is calculated for Mac

* Tidy up

* Support vibrancy and transparency for webview

Options Colour -> RGBA

* Rename vibrancy to appearance

* Add default appearance

* Refactor part 1

* Refactor Part 2

* Support Translucent Window Background

* Update runtime test

* Add IsDarkMode

Updated runtime test

* Support theme mode change event

* Misc fixes for events

* Support OnMultiple

* Small fixes to frontend events

* Add System calls to runtime

* Add system calls to js runtime

* Support System calls in Go Runtime

* Port Sync Store

* Refactor store. Add get().

* Refactor system. Add IsDarkMode state store

* Use IsDarkMode state store

* Remove generated files

* Support setting app state at startup

* Add Store to go runtime

* Update runtime to v1.0.3

* Remove unused event messages

* Debugging

* initial kitchen sink

* Fix right click crash

* Better drag support

* WIP

* Remove log package

* Add Log to Go runtime

* Add logging to kitchen sink

* Improved CodeBlock. Dark mode to store.

* Start Events. List styling moved to global scope.

* Make logger a public package

* Revert logger package

* Major logging refactor

* Make Ffenestri use logging subsystem.

* Debug refactor

* Add trace to JS runtime

* Migrate runtime to @wails

* Support Trace in kitchensink

* Support trace in go runtime

* Support log level

* Support Print in JS runtime

* Runtime v1.0.1

* Move Info message to Trace

* Support Print logging

* Updated Logger interface

* Fix number of methods in Log

* Support SetLogLevel() at runtime

Refactor of loglevel

* Made go runtime package public.

Using loglevel store to keep loglevel in sync

* Support dynamic loglevel

* Runtime refactor

* Fully refactored logging

* Better looking scrollbar

* Terminal output component

* Link component

* SetLogLevel fully supported

* Runtime defs update.

Slight System refactor

* More Logging updates

* Move preview for SetLogLevel

* Fix log level reactivity.

Misc fixes and tweaks

* logging: slight refactor

* Update logger constants to fix default values

* @wails/runtime v1.0.4

* Fix change in logging levels

* hook in windowWillClose

* refactor clilogger

* WIP Events.On

* Add Events.On

* Improved error handling?

* Disable annoying smart quotes

* update runtime definitions

* Support Emit & Once. Improved On.

* Remove old event methods

* Remove old Event methods

* Update runtime in kitchensink

* Revert Fatal on JS Error

* Tidy up events runtime

* Finish events page

* Update Browser runtime API

* Unify Browser runtime

* JS Runtime v1.0.8

* Fix browser runtime export

* Remove debug line

* Add Browser examples

* Update title

* Improved runtime.System

* Update runtime.System to make all methods

* Expose System methods in Go runtime

* Add System to kitchensink

* Huge improvement to calls: Now handles objects

* Add JS runtime Dialog

* Dialog WIP

* Js package generation (#554)

* WIP

* Generation of index.js

* Add RelativeToCwd

* Add JSDoc comments

* Convert to ES6 syntax

* Fix typo

* Initial generation of typescript declarations

* Typescript improvements

* Improved @returns jsdoc

* Improved declaration files

* Simplified output

* Rename file

* Tidy up

* Revert "Simplified output"

This reverts commit 15cdf7382b.

* Now parsing actual code

* Support Array types

* Reimagined parser

* Wrap parsing in Parser

* Rewritten module generator (TS Only)

* Final touches

* Slight refactor to improve output

* Struct comments. External struct literal binding

* Reworked project parser *working*

* remove debug info

* Refactor of parser

* remove the spew

* Better Ts support

* Better project generation logic

* Support local functions in bind()

* JS Object generation. Linting.

* Support json tags in module generation

* Updated mod files

* Support vscode file generation

* Better global.d.ts

* add ts-check to templates

* Support TS declaration files

* improved 'generate' command for module

Co-authored-by: Travis McLane <tmclane@gmail.com>
2020-11-15 09:27:23 +11:00

312 lines
6.6 KiB
Go

package parser
import (
"fmt"
"go/ast"
"strings"
"github.com/davecgh/go-spew/spew"
"github.com/fatih/structtag"
)
// Field defines a parsed struct field
type Field struct {
// Name of the field
Name string
// The type of the field.
// "struct" if it's a struct
Type string
// A pointer to the struct if the Type is "struct"
Struct *Struct
// User comments on the field
Comments []string
// Indicates if the Field is an array of type "Type"
IsArray bool
// JSON field name defined by a json tag
JSONOptions
}
type JSONOptions struct {
Name string
IsOptional bool
Ignored bool
}
// JSType returns the Javascript type for this field
func (f *Field) JSType() string {
return string(goTypeToJS(f))
}
// JSName returns the Javascript name for this field
func (f *Field) JSName() string {
if f.JSONOptions.Name != "" {
return f.JSONOptions.Name
}
return f.Name
}
// TSName returns the Typescript name for this field
func (f *Field) TSName() string {
result := f.Name
if f.JSONOptions.Name != "" {
result = f.JSONOptions.Name
}
if f.IsOptional {
result += "?"
}
return result
}
// AsTSDeclaration returns a TS definition of a single type field
func (f *Field) AsTSDeclaration(pkgName string) string {
return f.TSName() + ": " + f.TypeAsTSType(pkgName)
}
// NameForPropertyDoc returns a formatted name for the jsdoc @property declaration
func (f *Field) NameForPropertyDoc() string {
if f.IsOptional {
return "[" + f.JSName() + "]"
}
return f.JSName()
}
// TypeForPropertyDoc returns a formatted name for the jsdoc @property declaration
func (f *Field) TypeForPropertyDoc() string {
result := goTypeToJS(f)
if f.IsArray {
result += "[]"
}
return result
}
// TypeAsTSType converts the Field type to something TS wants
func (f *Field) TypeAsTSType(pkgName string) string {
var result = ""
switch f.Type {
case "string":
result = "string"
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
result = "number"
case "float32", "float64":
result = "number"
case "bool":
result = "boolean"
case "struct":
if f.Struct.Package != nil {
if f.Struct.Package.Name != pkgName {
result = f.Struct.Package.Name + "."
}
}
result = result + f.Struct.Name
default:
result = "any"
}
return result
}
func (p *Parser) parseField(file *ast.File, field *ast.Field, pkg *Package) ([]*Field, error) {
var result []*Field
var fieldType string
var strct *Struct
var isArray bool
var jsonOptions JSONOptions
// Determine type
switch t := field.Type.(type) {
case *ast.Ident:
fieldType = t.Name
unresolved := isUnresolvedType(fieldType)
// Check if this type is actually a struct
if unresolved {
// Assume it is a struct
// Parse the struct
var err error
strct, err = p.parseStruct(pkg, t.Name)
if err != nil {
return nil, err
}
if strct == nil {
fieldName := "<anonymous>"
if len(field.Names) > 0 {
fieldName = field.Names[0].Name
}
return nil, fmt.Errorf("unresolved type in field %s: %s", fieldName, fieldType)
}
fieldType = "struct"
}
case *ast.StarExpr:
fieldType = "struct"
packageName, structName, err := parseStructNameFromStarExpr(t)
if err != nil {
return nil, err
}
// If this is an external package, find it
if packageName != "" {
referencedGoPackage := pkg.getImportByName(packageName, file)
referencedPackage := p.getPackageByID(referencedGoPackage.ID)
// If we found the struct, save it as an external package reference
if referencedPackage != nil {
pkg.addExternalReference(referencedPackage)
}
// We save this to pkg anyway, because we want to know if this package
// was NOT found
pkg = referencedPackage
}
// If this is a package in our project, parse the struct!
if pkg != nil {
// Parse the struct
strct, err = p.parseStruct(pkg, structName)
if err != nil {
return nil, err
}
}
case *ast.ArrayType:
isArray = true
// Parse the Elt (There must be a better way!)
switch t := t.Elt.(type) {
case *ast.Ident:
fieldType = t.Name
case *ast.StarExpr:
fieldType = "struct"
packageName, structName, err := parseStructNameFromStarExpr(t)
if err != nil {
return nil, err
}
// If this is an external package, find it
if packageName != "" {
referencedGoPackage := pkg.getImportByName(packageName, file)
referencedPackage := p.getPackageByID(referencedGoPackage.ID)
// If we found the struct, save it as an external package reference
if referencedPackage != nil {
pkg.addExternalReference(referencedPackage)
}
// We save this to pkg anyway, because we want to know if this package
// was NOT found
pkg = referencedPackage
}
// If this is a package in our project, parse the struct!
if pkg != nil {
// Parse the struct
strct, err = p.parseStruct(pkg, structName)
if err != nil {
return nil, err
}
}
default:
// We will default to "Array<any>" for eg nested arrays
fieldType = "any"
}
default:
spew.Dump(t)
return nil, fmt.Errorf("unsupported field found in struct: %+v", t)
}
// Parse json tag if available
if field.Tag != nil {
err := parseJSONOptions(field.Tag.Value, &jsonOptions)
if err != nil {
return nil, err
}
}
// Loop over names if we have
if len(field.Names) > 0 {
for _, name := range field.Names {
// TODO: Check field names are valid in JS
if isJSReservedWord(name.Name) {
return nil, fmt.Errorf("unable to use field name %s - reserved word in Javascript", name.Name)
}
// Create a field per name
thisField := &Field{
Comments: parseComments(field.Doc),
}
thisField.Name = name.Name
thisField.Type = fieldType
thisField.Struct = strct
thisField.IsArray = isArray
thisField.JSONOptions = jsonOptions
result = append(result, thisField)
}
return result, nil
}
// When we have no name
thisField := &Field{
Comments: parseComments(field.Doc),
}
thisField.Type = fieldType
thisField.Struct = strct
thisField.IsArray = isArray
result = append(result, thisField)
return result, nil
}
func parseJSONOptions(fieldTag string, jsonOptions *JSONOptions) error {
// Remove backticks
fieldTag = strings.Trim(fieldTag, "`")
// Parse the tag
tags, err := structtag.Parse(fieldTag)
if err != nil {
return err
}
jsonTag, err := tags.Get("json")
if err != nil {
return err
}
if jsonTag == nil {
return nil
}
// Save the name
jsonOptions.Name = jsonTag.Name
// Check if this field is ignored
if jsonTag.Name == "-" {
jsonOptions.Ignored = true
}
// Check if this field is optional
if jsonTag.HasOption("omitempty") {
jsonOptions.IsOptional = true
}
return nil
}