vendor tsgo
This commit is contained in:
93
tools/tsgo/internal/lsp/server_semantictokens_test.go
Normal file
93
tools/tsgo/internal/lsp/server_semantictokens_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package lsp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/bundled"
|
||||
"github.com/microsoft/typescript-go/internal/lsp"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/lsptestutil"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
// TestSemanticTokensCRLF reproduces a crash where semantic tokens panics with
|
||||
// "token spans multiple lines" when the editor opens a file with CRLF line endings
|
||||
// but the project originally loaded the file from disk with LF line endings.
|
||||
//
|
||||
// The SourceFile AST keeps positions from the LF text, but the converter's
|
||||
// line map is recomputed from the CRLF overlay, causing a mismatch.
|
||||
func TestSemanticTokensCRLF(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !bundled.Embedded {
|
||||
t.Skip("bundled files are not embedded")
|
||||
}
|
||||
|
||||
// Enough lines so the cumulative \r\n vs \n offset difference
|
||||
// causes an LF-based position to land on a \r in the CRLF text.
|
||||
fileOnDisk := "var x\nvar x\nvar x\nvar x\nvar x\nvar x\nconst a = 1\n"
|
||||
fileFromEditor := strings.ReplaceAll(fileOnDisk, "\n", "\r\n")
|
||||
|
||||
files := map[string]string{
|
||||
"/home/projects/tsconfig.json": `{}`,
|
||||
"/home/projects/test.ts": fileOnDisk,
|
||||
"/home/projects/other.ts": "export {}",
|
||||
}
|
||||
fs := bundled.WrapFS(vfstest.FromMap(files, false))
|
||||
|
||||
onServerRequest := func(_ context.Context, req *lsproto.RequestMessage) *lsproto.ResponseMessage {
|
||||
if req.Method == lsproto.MethodClientRegisterCapability || req.Method == lsproto.MethodClientUnregisterCapability {
|
||||
return &lsproto.ResponseMessage{ID: req.ID, JSONRPC: req.JSONRPC, Result: lsproto.Null{}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
client, closeClient := lsptestutil.NewLSPClient(t, lsp.ServerOptions{
|
||||
Err: io.Discard, Cwd: "/home/projects", FS: fs, DefaultLibraryPath: bundled.LibPath(),
|
||||
}, onServerRequest)
|
||||
t.Cleanup(func() { _ = closeClient() })
|
||||
|
||||
initMsg, _, ok := lsptestutil.SendRequest(t, client, lsproto.InitializeInfo, &lsproto.InitializeParams{
|
||||
Capabilities: &lsproto.ClientCapabilities{
|
||||
TextDocument: &lsproto.TextDocumentClientCapabilities{
|
||||
SemanticTokens: &lsproto.SemanticTokensClientCapabilities{
|
||||
Requests: &lsproto.ClientSemanticTokensRequestOptions{
|
||||
Full: &lsproto.BooleanOrClientSemanticTokensRequestFullDelta{Boolean: new(true)},
|
||||
},
|
||||
TokenTypes: []string{"namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator"},
|
||||
TokenModifiers: []string{"declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary", "local"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Assert(t, ok && initMsg.AsResponse().Error == nil, "Initialize failed")
|
||||
lsptestutil.SendNotification(t, client, lsproto.InitializedInfo, &lsproto.InitializedParams{})
|
||||
<-client.Server.InitComplete()
|
||||
|
||||
// Open another project file to force the project to load test.ts from disk (LF).
|
||||
otherUri := lsproto.DocumentUri("file:///home/projects/other.ts")
|
||||
lsptestutil.SendNotification(t, client, lsproto.TextDocumentDidOpenInfo, &lsproto.DidOpenTextDocumentParams{
|
||||
TextDocument: &lsproto.TextDocumentItem{Uri: otherUri, LanguageId: "typescript", Text: files["/home/projects/other.ts"]},
|
||||
})
|
||||
msg1, _, _ := lsptestutil.SendRequest(t, client, lsproto.TextDocumentSemanticTokensFullInfo, &lsproto.SemanticTokensParams{
|
||||
TextDocument: lsproto.TextDocumentIdentifier{Uri: otherUri},
|
||||
})
|
||||
assert.Assert(t, msg1.AsResponse().Error == nil, "Initial request failed")
|
||||
|
||||
// Open test.ts with CRLF content; the project already parsed it from disk (LF).
|
||||
uri := lsproto.DocumentUri("file:///home/projects/test.ts")
|
||||
lsptestutil.SendNotification(t, client, lsproto.TextDocumentDidOpenInfo, &lsproto.DidOpenTextDocumentParams{
|
||||
TextDocument: &lsproto.TextDocumentItem{Uri: uri, LanguageId: "typescript", Text: fileFromEditor},
|
||||
})
|
||||
|
||||
// This panics: AST positions are LF-based but the line map is CRLF-based.
|
||||
msg, _, _ := lsptestutil.SendRequest(t, client, lsproto.TextDocumentSemanticTokensFullInfo, &lsproto.SemanticTokensParams{
|
||||
TextDocument: lsproto.TextDocumentIdentifier{Uri: uri},
|
||||
})
|
||||
if msg.AsResponse().Error != nil {
|
||||
t.Fatalf("Semantic tokens request failed: %s", msg.AsResponse().Error.Message)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user