Skip to content

Commit

Permalink
feat(tools/spxls): implement `textDocument/{prepareRename,rename,sign…
Browse files Browse the repository at this point in the history
…atureHelp,documentHighlight}` (#1146)

Updates #1059

Signed-off-by: Aofei Sheng <[email protected]>
  • Loading branch information
aofei authored Dec 16, 2024
1 parent 04fb361 commit a635c99
Show file tree
Hide file tree
Showing 16 changed files with 676 additions and 166 deletions.
7 changes: 1 addition & 6 deletions tools/spxls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ For detailed API references, please check the [index.d.ts](index.d.ts) file.
|| [`textDocument/didClose`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_didClose) | Removes document from server state and cleans up resources. |
| **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/inlayHint`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlayHint) | Provides inline hints (type information, parameter names). |
|| [`inlayHint/resolve`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#inlayHint_resolve) | Provides detailed information for selected inlay hints. |
|| [`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. |
Expand All @@ -57,16 +55,13 @@ For detailed API references, please check the [index.d.ts](index.d.ts) file.
|| [`textDocument/documentHighlight`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_documentHighlight) | Highlights other occurrences of selected symbol. |
|| [`textDocument/documentLink`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_documentLink) | Provides clickable links within document content. |
|| [`documentLink/resolve`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#documentLink_resolve) | Provides detailed target information for selected document links. |
|| [`textDocument/documentSymbol`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_documentSymbol) | Provides document symbols for outline/navigation. |
|| [`workspace/symbol`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#workspace_symbol) | Provides workspace-wide symbol search with name matching patterns. |
|| [`workspaceSymbol/resolve`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#workspace_symbolResolve) | Provides detailed information for selected workspace symbols. |
| **Code Quality** |||
|| [`textDocument/publishDiagnostics`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_publishDiagnostics) | Reports code errors and warnings in real-time. |
|| [`textDocument/diagnostic`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_diagnostic) | Pulls diagnostics for documents on request (pull model). |
|| [`workspace/diagnostic`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#workspace_diagnostic) | Pulls diagnostics for all workspace documents on request. |
|| [`textDocument/codeAction`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_codeAction) | Suggests fixes for diagnostics and code improvements. |
| **Code Modification** |||
|| [`textDocument/formatting`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_formatting) | Applies standardized formatting rules to document. |
|| [`textDocument/prepareRename`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_prepareRename) | Validates renaming possibility and returns valid range for the operation. |
|| [`textDocument/rename`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_rename) | Performs consistent symbol renaming across workspace. |
| **Semantic Features** |||
|| [`textDocument/semanticTokens/full`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#semanticTokens_fullRequest) | Provides semantic coloring for whole document. |
Expand Down
8 changes: 4 additions & 4 deletions tools/spxls/internal/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ package internal

import (
"fmt"
"go/token"
"go/types"
"sync"

"github.com/goplus/builder/tools/spxls/internal/pkgdata"
goptoken "github.com/goplus/gop/token"
"golang.org/x/tools/go/gcexportdata"
)

// importer implements [types.Importer].
type importer struct {
mu sync.Mutex
fset *token.FileSet
fset *goptoken.FileSet
loaded map[string]*types.Package
}

// NewImporter returns a new [types.Importer] that reads package data from the
// embedded pkgdata.
func NewImporter(fset *token.FileSet) types.Importer {
func NewImporter(fset *goptoken.FileSet) types.Importer {
if fset == nil {
fset = token.NewFileSet()
fset = goptoken.NewFileSet()
}
loaded := make(map[string]*types.Package)
loaded["unsafe"] = types.Unsafe
Expand Down
58 changes: 29 additions & 29 deletions tools/spxls/internal/server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,19 @@ func (s *Server) spxRenameResources(params []SpxRenameResourceParams) (*Workspac
result, err := s.compile()
if err != nil {
if errors.Is(err, errNoValidSpxFiles) || errors.Is(err, errNoMainSpxFile) {
return nil, nil // No valid spx files found in workspace.
return nil, nil
}
return nil, fmt.Errorf("failed to compile: %w", err)
}
if result.hasErrorSeverityDiagnostic {
return nil, errors.New("cannot rename spx resources when there are unresolved error severity diagnostics")
}

return s.spxRenameResourcesWithCompileResult(result, params)
}

// spxRenameResourcesWithCompileResult renames spx resources in the workspace with the given compile result.
func (s *Server) spxRenameResourcesWithCompileResult(result *compileResult, params []SpxRenameResourceParams) (*WorkspaceEdit, error) {
workspaceEdit := WorkspaceEdit{
Changes: make(map[DocumentURI][]TextEdit),
}
Expand Down Expand Up @@ -92,27 +97,22 @@ func (s *Server) spxRenameResources(params []SpxRenameResourceParams) (*Workspac

// spxGetDefinitions gets spx definitions at a specific position in a document.
func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefinitionIdentifier, error) {
result, err := s.compile()
if err != nil {
if errors.Is(err, errNoValidSpxFiles) || errors.Is(err, errNoMainSpxFile) {
return nil, nil // No valid spx files found in workspace.
}
return nil, fmt.Errorf("failed to compile: %w", err)
}
if l := len(params); l == 0 {
return nil, nil
} else if l > 1 {
return nil, errors.New("spx.getDefinitions only supports one document at a time")
}
param := params[0]

spxFile, err := s.fromDocumentURI(param.TextDocument.URI)
result, spxFile, astFile, err := s.compileAndGetASTFileForDocumentURI(param.TextDocument.URI)
if err != nil {
return nil, fmt.Errorf("failed to get spx file from document URI %q: %w", param.TextDocument.URI, err)
if errors.Is(err, errNoValidSpxFiles) || errors.Is(err, errNoMainSpxFile) {
return nil, nil
}
return nil, err
}
astFile, ok := result.mainPkgFiles[spxFile]
if !ok {
return nil, nil // No AST file found for the spx file, probably compile failed.
if astFile == nil {
return nil, nil
}
astFileScope := result.typeInfo.Scopes[astFile]

Expand All @@ -126,7 +126,7 @@ func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefin
if !pos.IsValid() {
return nil, nil
}
innermostScope := result.innermostScope(pos)
innermostScope := result.innermostScopeAt(pos)
universalScope := result.mainPkg.Scope().Parent()

var definitions []SpxDefinitionIdentifier
Expand All @@ -148,7 +148,7 @@ func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefin
}

// Find called event handlers.
calledEventHandlers := map[string]struct{}{}
calledEventHandlers := make(map[string]struct{})
for expr, tv := range result.typeInfo.Types {
if expr == nil || expr.Pos() == goptoken.NoPos || tv.IsType() {
continue // Skip type identifiers.
Expand All @@ -164,38 +164,38 @@ func (s *Server) spxGetDefinitions(params []SpxGetDefinitionsParams) ([]SpxDefin
if !ok {
continue
}
funIdent, ok := callExpr.Fun.(*gopast.Ident)
funcIdent, ok := callExpr.Fun.(*gopast.Ident)
if !ok {
continue
}
if !spxEventHandlerFuncNameRE.MatchString(funIdent.Name) {
if !spxEventHandlerFuncNameRE.MatchString(funcIdent.Name) {
continue
}
funObj := result.typeInfo.ObjectOf(funIdent)
if funObj == nil || funObj.Pkg() == nil || funObj.Pkg().Path() != spxPkgPath {
funcObj := result.typeInfo.ObjectOf(funcIdent)
if funcObj == nil || funcObj.Pkg() == nil || funcObj.Pkg().Path() != spxPkgPath {
continue
}
funTV, ok := result.typeInfo.Types[callExpr.Fun]
funcTV, ok := result.typeInfo.Types[callExpr.Fun]
if !ok {
continue
}
funSig, ok := funTV.Type.(*types.Signature)
funcSig, ok := funcTV.Type.(*types.Signature)
if !ok {
continue
}
funRecv := funSig.Recv()
if funRecv == nil {
funcRecv := funcSig.Recv()
if funcRecv == nil {
continue
}
recvType := funRecv.Type()
if ptr, ok := recvType.(*types.Pointer); ok {
recvType = ptr.Elem()
funcRecvType := funcRecv.Type()
if ptr, ok := funcRecvType.(*types.Pointer); ok {
funcRecvType = ptr.Elem()
}

if paramCount := funSig.Params().Len(); paramCount > 0 {
lastParamType := funSig.Params().At(paramCount - 1).Type()
if paramCount := funcSig.Params().Len(); paramCount > 0 {
lastParamType := funcSig.Params().At(paramCount - 1).Type()
if _, ok := lastParamType.(*types.Signature); ok {
calledEventHandlers[recvType.String()+"."+funIdent.Name] = struct{}{}
calledEventHandlers[funcRecvType.String()+"."+funcIdent.Name] = struct{}{}
}
}
}
Expand Down
Loading

0 comments on commit a635c99

Please sign in to comment.