Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tools/spxls): implement textDocument/completion #1169

Merged
merged 1 commit into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion tools/spxls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ For detailed API references, please check the [index.d.ts](index.d.ts) file.
| **Code Intelligence** |||
|| [`textDocument/hover`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_hover) | Shows types and documentation at cursor position. |
|| [`textDocument/completion`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_completion) | Generates context-aware code suggestions. |
|| [`completionItem/resolve`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#completionItem_resolve) | Provides detailed information for selected completion items. |
|| [`textDocument/signatureHelp`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_signatureHelp) | Shows function/method signature information. |
| **Symbols & Navigation** |||
|| [`textDocument/declaration`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_declaration) | Finds symbol declarations. |
Expand Down
25 changes: 23 additions & 2 deletions tools/spxls/internal/pkgdata/pkgdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"io/fs"
"strings"

"github.com/goplus/builder/tools/spxls/internal/pkgdoc"
)
Expand All @@ -17,13 +18,33 @@ import (
//go:embed pkgdata.zip
var pkgdataZip []byte

const (
pkgExportSuffix = ".pkgexport"
pkgDocSuffix = ".pkgdoc"
)

// ListPkgs lists all packages in the pkgdata.zip file.
func ListPkgs() ([]string, error) {
zr, err := zip.NewReader(bytes.NewReader(pkgdataZip), int64(len(pkgdataZip)))
if err != nil {
return nil, fmt.Errorf("failed to create zip reader: %w", err)
}
pkgs := make([]string, 0, len(zr.File)/2)
for _, f := range zr.File {
if strings.HasSuffix(f.Name, pkgExportSuffix) {
pkgs = append(pkgs, strings.TrimSuffix(f.Name, pkgExportSuffix))
}
}
return pkgs, nil
}

// OpenExport opens a package export file.
func OpenExport(pkgPath string) (io.ReadCloser, error) {
zr, err := zip.NewReader(bytes.NewReader(pkgdataZip), int64(len(pkgdataZip)))
if err != nil {
return nil, fmt.Errorf("failed to create zip reader: %w", err)
}
pkgExportFile := pkgPath + ".pkgexport"
pkgExportFile := pkgPath + pkgExportSuffix
for _, f := range zr.File {
if f.Name == pkgExportFile {
return f.Open()
Expand All @@ -38,7 +59,7 @@ func GetPkgDoc(pkgPath string) (*pkgdoc.PkgDoc, error) {
if err != nil {
return nil, fmt.Errorf("failed to create zip reader: %w", err)
}
pkgDocFile := pkgPath + ".pkgdoc"
pkgDocFile := pkgPath + pkgDocSuffix
for _, f := range zr.File {
if f.Name == pkgDocFile {
rc, err := f.Open()
Expand Down
6 changes: 1 addition & 5 deletions tools/spxls/internal/server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,6 @@ func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefin
return nil, err
}
if astFile == nil {
diagnostics := result.diagnostics[param.TextDocument.URI]
if len(diagnostics) > 0 {
return nil, fmt.Errorf("failed to compile file: %s", diagnostics[0].Message)
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当时在 https://github.com/goplus/builder/pull/1162/files#diff-0c6691e1482b8fb7fab79c84814456d80895bd823bc74fee320c1dc82c70ea9f 添加这个处理,是因为有语法错误时 astFilenil 对吧?

这个 pr 调整了 compile 的逻辑,现在有语法错误时也能正常拿到 astFile 了。

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

添加这个处理,是因为有语法错误时 astFilenil 对吧?

是的

这个 pr 调整了 compile 的逻辑,现在有语法错误时也能正常拿到 astFile 了。

棒 👍

return nil, nil
}
astFileScope := result.typeInfo.Scopes[astFile]
Expand Down Expand Up @@ -233,7 +229,7 @@ func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefin
})
case *types.Func:
var methodNames []string
if methodOverloads := expandGoptOverloadedMethod(member); len(methodOverloads) > 0 {
if methodOverloads := expandGopOverloadedFunc(member); len(methodOverloads) > 0 {
methodNames = make([]string, 0, len(methodOverloads))
for _, method := range methodOverloads {
_, methodName, _ := util.SplitGoptMethod(method.Name())
Expand Down
40 changes: 40 additions & 0 deletions tools/spxls/internal/server/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,46 @@ onStart => {
}))
})

t.Run("ParseError", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`
// Invalid syntax
var (
MySprite Sprite
`),
}
}), nil)

mainSpxFileScopeParams := []SpxGetDefinitionsParams{
{
TextDocumentPositionParams: TextDocumentPositionParams{
TextDocument: TextDocumentIdentifier{URI: "file:///main.spx"},
Position: Position{Line: 0, Character: 0},
},
},
}
mainSpxFileScopeDefs, err := s.spxGetDefinitions(mainSpxFileScopeParams)
require.NoError(t, err)
require.NotNil(t, mainSpxFileScopeDefs)
assert.True(t, spxDefinitionIdentifierSliceContains(mainSpxFileScopeDefs, SpxDefinitionIdentifier{
Package: util.ToPtr("builtin"),
Name: util.ToPtr("println"),
}))
assert.True(t, spxDefinitionIdentifierSliceContains(mainSpxFileScopeDefs, SpxDefinitionIdentifier{
Package: util.ToPtr("main"),
Name: util.ToPtr("MySprite"),
}))
assert.True(t, spxDefinitionIdentifierSliceContains(mainSpxFileScopeDefs, SpxDefinitionIdentifier{
Package: util.ToPtr(spxPkgPath),
Name: util.ToPtr("Game.onStart"),
}))
assert.False(t, spxDefinitionIdentifierSliceContains(mainSpxFileScopeDefs, SpxDefinitionIdentifier{
Package: util.ToPtr(spxPkgPath),
Name: util.ToPtr("Sprite.onStart"),
}))
})

t.Run("TrailingEmptyLinesOfSpriteCode", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
Expand Down
39 changes: 18 additions & 21 deletions tools/spxls/internal/server/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,12 @@ func (s *Server) compile() (*compileResult, error) {
Mode: gopparser.AllErrors | gopparser.ParseComments,
})
if err != nil {
// Handle parse errors.
var parseErr gopscanner.ErrorList
var (
parseErr gopscanner.ErrorList
codeErr *gogen.CodeError
)
if errors.As(err, &parseErr) {
// Handle parse errors.
for _, e := range parseErr {
result.addDiagnostics(documentURI, Diagnostic{
Severity: SeverityError,
Expand All @@ -351,12 +354,8 @@ func (s *Server) compile() (*compileResult, error) {
Message: e.Msg,
})
}
continue
}

// Handle code generation errors.
var codeErr *gogen.CodeError
if errors.As(err, &codeErr) {
} else if errors.As(err, &codeErr) {
// Handle code generation errors.
position := codeErr.Fset.Position(codeErr.Pos)
result.addDiagnostics(documentURI, Diagnostic{
Severity: SeverityError,
Expand All @@ -366,21 +365,19 @@ func (s *Server) compile() (*compileResult, error) {
},
Message: codeErr.Error(),
})
continue
} else {
// Handle unknown errors.
result.addDiagnostics(documentURI, Diagnostic{
Severity: SeverityError,
Range: Range{
Start: Position{Line: 0, Character: 0},
End: Position{Line: 0, Character: 0},
},
Message: fmt.Sprintf("failed to parse spx file: %v", err),
})
}

// Handle unknown errors.
result.addDiagnostics(documentURI, Diagnostic{
Severity: SeverityError,
Range: Range{
Start: Position{Line: 0, Character: 0},
End: Position{Line: 0, Character: 0},
},
Message: fmt.Sprintf("failed to parse spx file: %v", err),
})
continue
}
if astFile.Name.Name == "main" {
if astFile != nil && astFile.Name.Name == "main" {
result.mainASTPkg.Files[spxFile] = astFile
if spxFileBaseName := path.Base(spxFile); spxFileBaseName == "main.spx" {
result.mainSpxFile = spxFile
Expand Down
Loading
Loading