Skip to content

Commit

Permalink
fix(tools/spxls): resolve issues with LS functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
aofei committed Jan 13, 2025
1 parent 700dcad commit 16438da
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 286 deletions.
17 changes: 8 additions & 9 deletions tools/ispx/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/goplus/builder/ispx
go 1.21

require (
github.com/goplus/igop v0.27.1
github.com/goplus/igop v0.28.0
github.com/goplus/reflectx v1.2.2
github.com/goplus/spx v1.0.1-0.20241029011511-845f2c0e2e74
github.com/hajimehoshi/ebiten/v2 v2.8.0-alpha.3
Expand All @@ -19,35 +19,34 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/goplus/canvas v0.1.0 // indirect
github.com/goplus/gogen v1.15.2 // indirect
github.com/goplus/gogen v1.16.6-0.20250112152508-dc8cddfd52df // indirect
github.com/goplus/gop v1.2.6 // indirect
github.com/goplus/mod v0.13.10 // indirect
github.com/goplus/mod v0.13.15 // indirect
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
github.com/jezek/xgb v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/qiniu/audio v0.2.1 // indirect
github.com/qiniu/x v1.13.10 // indirect
github.com/qiniu/x v1.13.11 // indirect
github.com/srwiley/oksvg v0.0.0-20210519022825-9fc0c575d5fe // indirect
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 // indirect
github.com/timandy/routine v1.1.1 // indirect
github.com/timandy/routine v1.1.4 // indirect
github.com/visualfc/funcval v0.1.4 // indirect
github.com/visualfc/gid v0.1.0 // indirect
github.com/visualfc/gid v0.2.0 // indirect
github.com/visualfc/goembed v0.3.2 // indirect
github.com/visualfc/xtype v0.2.0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.23.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)

replace (
github.com/goplus/gop => github.com/goplus/gop v1.2.0-pre.1.0.20250112163018-5fb12b1b2972
github.com/hajimehoshi/oto => github.com/hajimehoshi/oto v1.0.1
github.com/srwiley/oksvg => github.com/qiniu/oksvg v0.2.0-no-charset
golang.org/x/image => golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
golang.org/x/mobile => golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5
golang.org/x/mod => golang.org/x/mod v0.5.1
)
69 changes: 46 additions & 23 deletions tools/ispx/go.sum

Large diffs are not rendered by default.

13 changes: 5 additions & 8 deletions tools/spxls/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ module github.com/goplus/builder/tools/spxls
go 1.23.4

require (
github.com/goplus/gogen v1.15.2
github.com/goplus/gop v1.2.6
github.com/goplus/mod v0.13.10
github.com/goplus/gogen v1.16.6-0.20250112152508-dc8cddfd52df
github.com/goplus/gop v1.2.0-pre.1.0.20250112163018-5fb12b1b2972
github.com/goplus/mod v0.13.15
github.com/goplus/spx v1.1.0
github.com/stretchr/testify v1.9.0
golang.org/x/tools v0.23.0
)

// https://github.com/nighca/mod/tree/web TODO: any better way to fix this?
replace github.com/goplus/mod v0.13.10 => github.com/nighca/mod v0.0.0-20240805065729-b50535825ae2

require (
github.com/ajstarks/svgo v0.0.0-20210927141636-6d70534b1098 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -30,12 +27,12 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/qiniu/audio v0.2.1 // indirect
github.com/qiniu/x v1.13.10 // indirect
github.com/qiniu/x v1.13.11 // indirect
github.com/srwiley/oksvg v0.0.0-20210519022825-9fc0c575d5fe // indirect
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
Expand Down
20 changes: 10 additions & 10 deletions tools/spxls/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/goplus/canvas v0.1.0 h1:Vx3f2+U8UANvWf5/01YsQYKNbZDm1GZCjhlEBFrQkeU=
github.com/goplus/canvas v0.1.0/go.mod h1:Rhcvo5qkpD9WuXFnvnXtrBSY97l6h7sXQuofrmiLNdM=
github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM=
github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
github.com/goplus/gop v1.2.6 h1:kog3c5Js+8EopqmI4+CwueXsqibnBwYVt5q5N7juRVY=
github.com/goplus/gop v1.2.6/go.mod h1:uREWbR1MrFaviZ4Mbx4ZCcAYDoqzO0iv1Qo6Np0Xx4E=
github.com/goplus/gogen v1.16.6-0.20250112152508-dc8cddfd52df h1:YIcLq6a7eGp3niBGF/VQJbSjos2MtnaUu+ysj6Iuhjo=
github.com/goplus/gogen v1.16.6-0.20250112152508-dc8cddfd52df/go.mod h1:6TQYbabXDF9LCdDkOOzHmfg1R4ENfXQ3XpHa9RhTSD8=
github.com/goplus/gop v1.2.0-pre.1.0.20250112163018-5fb12b1b2972 h1:wA4+I+DDfoNwR+zCnaeckt5pxZgLAuaKvVkC+T9GdiQ=
github.com/goplus/gop v1.2.0-pre.1.0.20250112163018-5fb12b1b2972/go.mod h1:lcW75c0a5v361jId1Vxs4lRrsasWsQDH0k3dG3Z7wH0=
github.com/goplus/mod v0.13.15 h1:IyneSjwm1VpwvHGz6hSHnFxZCuO6ULHcu74IAZcW9nw=
github.com/goplus/mod v0.13.15/go.mod h1:invR72Rz2+qpOOsXqxz830MX8/aR2GDR2EAow/WgfHI=
github.com/goplus/spx v1.1.0 h1:rke7F7YpbjeI583HkuNiUGmsbYdrloNT1ABYLfgv9i8=
github.com/goplus/spx v1.1.0/go.mod h1:1hkX9vPdDHrsA6LG8itiLMp83KIDJ0KlJI9CmpCoiCA=
github.com/hajimehoshi/ebiten/v2 v2.7.9 h1:DYH/usAa9dMHcGkBIIEApJsVqDekrJBxYHmsBuly8Iw=
Expand All @@ -37,17 +39,15 @@ github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/Sisy
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/nighca/mod v0.0.0-20240805065729-b50535825ae2 h1:Z3kt9W9vV7yx+44TNwl8+Lc0a8zr6ptN/RzswphNlD0=
github.com/nighca/mod v0.0.0-20240805065729-b50535825ae2/go.mod h1:fyCcoiL02uUQK9CWxGK9pQzuJT+rZIvRKaaG+hSa2bk=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qiniu/audio v0.2.1 h1:lAc3dWfr7uAfn7mfee2u0/fl/QSQA9oTOqdBtxyFZAM=
github.com/qiniu/audio v0.2.1/go.mod h1:APMJRPaS4toviejZnDzzZ8wVyr12jqZhd3xfKr/qYnE=
github.com/qiniu/x v1.11.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE=
github.com/qiniu/x v1.13.10/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
github.com/qiniu/x v1.13.11 h1:zlFLY9zFXOwKEjx0SQXRBunhFjDVpEY+pRGL/4shd5U=
github.com/qiniu/x v1.13.11/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
github.com/srwiley/oksvg v0.0.0-20210519022825-9fc0c575d5fe h1:J5Ga/gb+4/WgJBupg9Fp8F6JQnUT3UF+asoTweLi9Jc=
github.com/srwiley/oksvg v0.0.0-20210519022825-9fc0c575d5fe/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780 h1:oDMiXaTMyBEuZMU53atpxqYsSB3U1CHkeAu2zr6wTeY=
Expand All @@ -74,8 +74,8 @@ golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd/go.mod h1:pe2sM7Uk+2Su1y7
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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=
Expand Down
178 changes: 94 additions & 84 deletions tools/spxls/internal/server/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ type compileResult struct {
// mainPkg is the main package.
mainPkg *types.Package

// mainPkgGameType is the Game type in the main package.
mainPkgGameType *types.Named

// mainPkgSpriteTypes stores sprite types in the main package.
mainPkgSpriteTypes []*types.Named

// mainASTPkg is the main package AST.
mainASTPkg *gopast.Package

Expand All @@ -80,9 +86,6 @@ type compileResult struct {
// process.
typeInfo *goptypesutil.Info

// spxSpriteNames stores spx sprite names.
spxSpriteNames []string

// spxResourceSet is the set of spx resources.
spxResourceSet SpxResourceSet

Expand Down Expand Up @@ -342,6 +345,29 @@ func (r *compileResult) selectorTypeNameForIdent(ident *gopast.Ident) string {
return ""
}

// isGameOrSpriteThisPtr reports whether the given object is the "this" pointer
// of Game or a Sprite type method in the main package.
func (r *compileResult) isGameOrSpriteThisPtr(obj types.Object) bool {
if obj == nil || obj.Pkg() == nil || obj.Pkg().Path() != "main" || obj.Name() != "this" {
return false
}

v, ok := obj.(*types.Var)
if !ok {
return false
}
ptr, ok := v.Type().(*types.Pointer)
if !ok {
return false
}

named, ok := ptr.Elem().(*types.Named)
if !ok {
return false
}
return named == r.mainPkgGameType || slices.Contains(r.mainPkgSpriteTypes, named)
}

// isDefinedInFirstVarBlock reports whether the given object is defined in the
// first var block of an AST file.
func (r *compileResult) isDefinedInFirstVarBlock(obj types.Object) bool {
Expand Down Expand Up @@ -591,7 +617,10 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi
diagnostics: make(map[DocumentURI][]Diagnostic, len(spxFiles)),
}

gpfs := vfs.NewGopParserFS(snapshot)
var (
gpfs = vfs.NewGopParserFS(snapshot)
spriteNames = make([]string, 0, len(spxFiles)-1)
)
for _, spxFile := range spxFiles {
documentURI := s.toDocumentURI(spxFile)
result.diagnostics[documentURI] = []Diagnostic{}
Expand Down Expand Up @@ -644,7 +673,7 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi
if spxFileBaseName := path.Base(spxFile); spxFileBaseName == "main.spx" {
result.mainSpxFile = spxFile
} else {
result.spxSpriteNames = append(result.spxSpriteNames, strings.TrimSuffix(spxFileBaseName, ".spx"))
spriteNames = append(spriteNames, strings.TrimSuffix(spxFileBaseName, ".spx"))
}

for _, decl := range astFile.Decls {
Expand Down Expand Up @@ -707,12 +736,23 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi
},
nil,
result.typeInfo,
).Files(nil, gopASTFileMapToSlice(result.mainASTPkg.Files)); err != nil {
).Files(nil, slices.Collect(maps.Values(result.mainASTPkg.Files))); err != nil {
// Errors should be handled by the type checker.
}

if obj := result.mainPkg.Scope().Lookup("Game"); obj != nil {
result.mainPkgGameType = obj.Type().(*types.Named)
}

result.mainPkgSpriteTypes = make([]*types.Named, 0, len(spriteNames))
for _, spxSpriteName := range spriteNames {
if obj := result.mainPkg.Scope().Lookup(spxSpriteName); obj != nil {
result.mainPkgSpriteTypes = append(result.mainPkgSpriteTypes, obj.Type().(*types.Named))
}
}

s.inspectForSpxResourceSet(snapshot, result)
s.inspectForSpxResourceAutoBindingsAndRefsAtDecls(result)
s.inspectForSpxResourceAutoBindings(result)
s.inspectForSpxResourceRefs(result)

return result, nil
Expand Down Expand Up @@ -791,99 +831,69 @@ func (s *Server) inspectForSpxResourceSet(snapshot *vfs.MapFS, result *compileRe
result.spxResourceSet = *spxResourceSet
}

// inspectForSpxResourceAutoBindingsAndRefsAtDecls inspects for spx resource
// auto-bindings and references at variable or constant declarations.
func (s *Server) inspectForSpxResourceAutoBindingsAndRefsAtDecls(result *compileResult) {
// inspectForSpxResourceAutoBindings inspects for spx resource auto-bindings and
// references at variable or constant declarations.
func (s *Server) inspectForSpxResourceAutoBindings(result *compileResult) {
mainSpxFileScope := result.typeInfo.Scopes[result.mainASTPkg.Files[result.mainSpxFile]]
for ident, obj := range result.typeInfo.Defs {
objType, ok := obj.Type().(*types.Named)
v, ok := obj.(*types.Var)
if !ok {
continue
}
varType, ok := v.Type().(*types.Named)
if !ok {
continue
}

spxFile := result.fset.Position(ident.Pos()).Filename
isInMainSpx := spxFile == result.mainSpxFile
documentURI := s.toDocumentURI(spxFile)

identRange := Range{
Start: FromGopTokenPosition(result.fset.Position(ident.Pos())),
End: FromGopTokenPosition(result.fset.Position(ident.End())),
if spxFile != result.mainSpxFile || result.innermostScopeAt(ident.Pos()) != mainSpxFileScope {
continue
}

var (
isSpxSoundResourceAutoBinding bool
isSpxSpriteResourceAutoBinding bool
)
switch objTypeName := objType.String(); objTypeName {
switch varType.String() {
case spxSoundTypeFullName:
isSpxSoundResourceAutoBinding = true
isSpxSoundResourceAutoBinding = result.spxResourceSet.Sound(v.Name()) != nil
case spxSpriteTypeFullName:
isSpxSpriteResourceAutoBinding = true
isSpxSpriteResourceAutoBinding = result.spxResourceSet.Sprite(v.Name()) != nil
default:
for _, spxSpriteName := range result.spxSpriteNames {
if objTypeName != "main."+spxSpriteName {
continue
}
if ident.Name != spxSpriteName {
result.addDiagnostics(documentURI, Diagnostic{
Severity: SeverityError,
Range: identRange,
Message: "sprite resource name must match type name for explicit auto-binding to work",
})
continue
}
isSpxSpriteResourceAutoBinding = true
break
}
isSpxSpriteResourceAutoBinding = v.Name() == varType.Obj().Name() && slices.Contains(result.mainPkgSpriteTypes, varType)
}
if isSpxSoundResourceAutoBinding || isSpxSpriteResourceAutoBinding {
var diags []Diagnostic
if isInMainSpx {
if result.isDefinedInFirstVarBlock(obj) {
switch {
case isSpxSoundResourceAutoBinding:
result.addSpxResourceRef(SpxResourceRef{
ID: SpxSoundResourceID{SoundName: ident.Name},
Kind: SpxResourceRefKindAutoBinding,
Node: ident,
})
result.spxSoundResourceAutoBindings[obj] = struct{}{}
if result.spxResourceSet.Sound(ident.Name) == nil {
diags = []Diagnostic{{
Severity: SeverityError,
Range: identRange,
Message: fmt.Sprintf("sound resource %q not found", ident.Name),
}}
}
case isSpxSpriteResourceAutoBinding:
result.addSpxResourceRef(SpxResourceRef{
ID: SpxSpriteResourceID{SpriteName: ident.Name},
Kind: SpxResourceRefKindAutoBinding,
Node: ident,
})
result.spxSpriteResourceAutoBindings[obj] = struct{}{}
if result.spxResourceSet.Sprite(ident.Name) == nil {
diags = []Diagnostic{{
Severity: SeverityError,
Range: identRange,
Message: fmt.Sprintf("sprite resource %q not found", ident.Name),
}}
}
}
} else {
diags = []Diagnostic{{
Severity: SeverityWarning,
Range: identRange,
Message: "resources must be defined in the first var block for auto-binding",
}}
}
} else {
diags = []Diagnostic{{
Severity: SeverityWarning,
Range: identRange,
Message: "auto-binding of resources can only happen in main.spx",
}}
}
result.addDiagnostics(documentURI, diags...)
if !isSpxSoundResourceAutoBinding && !isSpxSpriteResourceAutoBinding {
continue
}

if !result.isDefinedInFirstVarBlock(obj) {
documentURI := s.toDocumentURI(spxFile)
result.addDiagnostics(documentURI, Diagnostic{
Severity: SeverityWarning,
Range: Range{
Start: FromGopTokenPosition(result.fset.Position(ident.Pos())),
End: FromGopTokenPosition(result.fset.Position(ident.End())),
},
Message: "resources must be defined in the first var block for auto-binding",
})
continue
}

switch {
case isSpxSoundResourceAutoBinding:
result.addSpxResourceRef(SpxResourceRef{
ID: SpxSoundResourceID{SoundName: ident.Name},
Kind: SpxResourceRefKindAutoBinding,
Node: ident,
})
result.spxSoundResourceAutoBindings[obj] = struct{}{}
case isSpxSpriteResourceAutoBinding:
result.addSpxResourceRef(SpxResourceRef{
ID: SpxSpriteResourceID{SpriteName: ident.Name},
Kind: SpxResourceRefKindAutoBinding,
Node: ident,
})
result.spxSpriteResourceAutoBindings[obj] = struct{}{}
}
}
}
Expand Down
Loading

0 comments on commit 16438da

Please sign in to comment.