Skip to content

Commit

Permalink
fix(spxls): add missing JSON marshalers and unmarshalers (#1156)
Browse files Browse the repository at this point in the history
Signed-off-by: Aofei Sheng <[email protected]>
  • Loading branch information
aofei authored Dec 19, 2024
1 parent 87be919 commit 3aa09e4
Show file tree
Hide file tree
Showing 12 changed files with 2,544 additions and 350 deletions.
3 changes: 1 addition & 2 deletions spx-gui/src/components/editor/code-editor/lsp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export class SpxLSPClient extends Disposable {

async textDocumentDiagnostic(params: lsp.DocumentDiagnosticParams): Promise<lsp.DocumentDiagnosticReport> {
const spxlc = await this.prepareRquest()
const report = await spxlc.request<any>(lsp.DocumentDiagnosticRequest.method, params)
return report.value // TODO: this should be fixed in `spxls`, see type `DocumentDiagnosticReport` there for more details
return spxlc.request<any>(lsp.DocumentDiagnosticRequest.method, params)
}
}
2 changes: 1 addition & 1 deletion tools/spxls/internal/server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefin
// Find called event handlers.
calledEventHandlers := make(map[string]struct{})
for expr, tv := range result.typeInfo.Types {
if expr == nil || expr.Pos() == goptoken.NoPos || tv.IsType() {
if expr == nil || !expr.Pos().IsValid() || tv.IsType() {
continue // Skip type identifiers.
}
if expr.Pos() < goptoken.Pos(tokenFile.Base()) ||
Expand Down
14 changes: 7 additions & 7 deletions tools/spxls/internal/server/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func (r *compileResult) refIdentsOf(obj types.Object) []*gopast.Ident {
return idents
}

// isInMainSpxFirstVarBlock returns true if the given position is in the first
// isInMainSpxFirstVarBlock reports whether the given position is in the first
// var block in main.spx.
func (r *compileResult) isInMainSpxFirstVarBlock(pos goptoken.Pos) bool {
return r.mainSpxFirstVarBlock != nil &&
Expand All @@ -224,11 +224,11 @@ func (r *compileResult) spxResourceRefAtASTFilePosition(astFile *gopast.File, po
if node == nil {
return true
}
nodePos := r.fset.Position(node.Pos())
nodeEnd := r.fset.Position(node.End())
if tokenPos.Line == nodePos.Line &&
tokenPos.Column >= nodePos.Column &&
tokenPos.Column <= nodeEnd.Column {
startPos := r.fset.Position(node.Pos())
endPos := r.fset.Position(node.End())
if tokenPos.Line == startPos.Line &&
tokenPos.Column >= startPos.Column &&
tokenPos.Column <= endPos.Column {
if foundNode == nil || (node.Pos() >= foundNode.Pos() && node.End() <= foundNode.End()) {
foundNode = node
}
Expand Down Expand Up @@ -601,7 +601,7 @@ func (s *Server) inspectForSpxResourceAutoBindingsAndRefsAtDecls(result *compile
func (s *Server) inspectForSpxResourceRefs(result *compileResult) {
// Check all type-checked expressions.
for expr, tv := range result.typeInfo.Types {
if expr == nil || expr.Pos() == goptoken.NoPos || tv.IsType() {
if expr == nil || !expr.Pos().IsValid() || tv.IsType() {
continue // Skip type identifiers.
}

Expand Down
71 changes: 71 additions & 0 deletions tools/spxls/internal/server/definition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package server

import (
"errors"
"go/types"
)

// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_declaration
func (s *Server) textDocumentDeclaration(params *DeclarationParams) (any, error) {
return s.textDocumentDefinition(&DefinitionParams{
TextDocumentPositionParams: params.TextDocumentPositionParams,
WorkDoneProgressParams: params.WorkDoneProgressParams,
PartialResultParams: params.PartialResultParams,
})
}

// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_definition
func (s *Server) textDocumentDefinition(params *DefinitionParams) (any, error) {
result, _, astFile, err := s.compileAndGetASTFileForDocumentURI(params.TextDocument.URI)
if err != nil {
if errors.Is(err, errNoValidSpxFiles) || errors.Is(err, errNoMainSpxFile) {
return nil, nil
}
return nil, err
}
if astFile == nil {
return nil, nil
}

_, obj := result.identAndObjectAtASTFilePosition(astFile, params.Position)
if !isMainPkgObject(obj) {
return nil, nil
}

defIdent := result.defIdentOf(obj)
if defIdent == nil {
return nil, nil
}
return s.createLocationFromIdent(result.fset, defIdent), nil
}

// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_typeDefinition
func (s *Server) textDocumentTypeDefinition(params *TypeDefinitionParams) (any, error) {
result, _, astFile, err := s.compileAndGetASTFileForDocumentURI(params.TextDocument.URI)
if err != nil {
if errors.Is(err, errNoValidSpxFiles) || errors.Is(err, errNoMainSpxFile) {
return nil, nil
}
return nil, err
}
if astFile == nil {
return nil, nil
}

_, obj := result.identAndObjectAtASTFilePosition(astFile, params.Position)
if !isMainPkgObject(obj) {
return nil, nil
}

objType := unwrapPointerType(obj.Type())
named, ok := objType.(*types.Named)
if !ok {
return nil, nil
}

objPos := named.Obj().Pos()
if !objPos.IsValid() {
return nil, nil
}
return s.createLocationFromPos(result.fset, objPos), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,202 +202,3 @@ var x int
require.Nil(t, def)
})
}

func TestServerTextDocumentImplementation(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`
type MyInterface interface {
myMethod()
}
type MyType struct{}
func (t MyType) myMethod() {}
type MyType2 struct{}
func (t MyType2) myMethod() {}
var x MyInterface
`),
}
}), nil)

implementations, err := s.textDocumentImplementation(&ImplementationParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 2, Character: 1},
},
})
require.NoError(t, err)
require.NotNil(t, implementations)
locations, ok := implementations.([]Location)
require.True(t, ok)
require.Len(t, locations, 2)
assert.Contains(t, locations, Location{
URI: "file:///main.spx",
Range: Range{
Start: Position{Line: 7, Character: 16},
End: Position{Line: 7, Character: 16},
},
})
assert.Contains(t, locations, Location{
URI: "file:///main.spx",
Range: Range{
Start: Position{Line: 11, Character: 17},
End: Position{Line: 11, Character: 17},
},
})
})

t.Run("NonInterfaceMethod", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`
type MyType struct{}
func (t MyType) myMethod() {}
`),
}
}), nil)

implementation, err := s.textDocumentImplementation(&ImplementationParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 3, Character: 16},
},
})
require.NoError(t, err)
require.NotNil(t, implementation)
location, ok := implementation.(Location)
require.True(t, ok)
assert.Equal(t, Location{
URI: "file:///main.spx",
Range: Range{
Start: Position{Line: 3, Character: 16},
End: Position{Line: 3, Character: 16},
},
}, location)
})

t.Run("InvalidPosition", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`
type MyType struct{}
`),
}
}), nil)

implementation, err := s.textDocumentImplementation(&ImplementationParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 99, Character: 99},
},
})
require.NoError(t, err)
require.Nil(t, implementation)
})
}

func TestServerTextDocumentReferences(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`
var (
MySprite Sprite
)
MySprite.turn Left
run "assets", {Title: "My Game"}
`),
"MySprite.spx": []byte(`
onStart => {
MySprite.turn Right
}
`),
"assets/sprites/MySprite/index.json": []byte(`{}`),
}
}), nil)

mainSpxMySpriteDef, err := s.textDocumentReferences(&ReferenceParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 2, Character: 2},
},
Context: ReferenceContext{
IncludeDeclaration: true,
},
})
require.NoError(t, err)
require.NotNil(t, mainSpxMySpriteDef)
require.Len(t, mainSpxMySpriteDef, 3)
assert.Contains(t, mainSpxMySpriteDef, Location{
URI: "file:///main.spx",
Range: Range{
Start: Position{Line: 2, Character: 1},
End: Position{Line: 2, Character: 9},
},
})
assert.Contains(t, mainSpxMySpriteDef, Location{
URI: "file:///main.spx",
Range: Range{
Start: Position{Line: 4, Character: 0},
End: Position{Line: 4, Character: 8},
},
})
assert.Contains(t, mainSpxMySpriteDef, Location{
URI: "file:///MySprite.spx",
Range: Range{
Start: Position{Line: 2, Character: 1},
End: Position{Line: 2, Character: 9},
},
})

mainSpxTurnDef, err := s.textDocumentReferences(&ReferenceParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 4, Character: 9},
},
Context: ReferenceContext{
IncludeDeclaration: true,
},
})
require.NoError(t, err)
require.NotNil(t, mainSpxTurnDef)
require.Len(t, mainSpxTurnDef, 2)
assert.Contains(t, mainSpxTurnDef, Location{
URI: "file:///main.spx",
Range: Range{
Start: Position{Line: 4, Character: 9},
End: Position{Line: 4, Character: 13},
},
})
assert.Contains(t, mainSpxTurnDef, Location{
URI: "file:///MySprite.spx",
Range: Range{
Start: Position{Line: 2, Character: 10},
End: Position{Line: 2, Character: 14},
},
})
})

t.Run("InvalidPosition", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`var x int`),
}
}), nil)

refs, err := s.textDocumentReferences(&ReferenceParams{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 99, Character: 99},
},
})
require.NoError(t, err)
require.Nil(t, refs)
})
}
Loading

0 comments on commit 3aa09e4

Please sign in to comment.