remove unused packages
This commit is contained in:
parent
079ff1ee3e
commit
b1f5661baf
@ -1,320 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/api/encoder"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/astnav"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ls"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/project"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/project/logging"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs"
|
|
||||||
"github.com/go-json-experiment/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type handleMap[T any] map[Handle[T]]*T
|
|
||||||
|
|
||||||
type APIInit struct {
|
|
||||||
Logger logging.Logger
|
|
||||||
FS vfs.FS
|
|
||||||
SessionOptions *project.SessionOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
type API struct {
|
|
||||||
logger logging.Logger
|
|
||||||
session *project.Session
|
|
||||||
|
|
||||||
projects map[Handle[project.Project]]tspath.Path
|
|
||||||
filesMu sync.Mutex
|
|
||||||
files handleMap[ast.SourceFile]
|
|
||||||
symbolsMu sync.Mutex
|
|
||||||
symbols handleMap[ast.Symbol]
|
|
||||||
typesMu sync.Mutex
|
|
||||||
types handleMap[checker.Type]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAPI(init *APIInit) *API {
|
|
||||||
api := &API{
|
|
||||||
session: project.NewSession(&project.SessionInit{
|
|
||||||
Logger: init.Logger,
|
|
||||||
FS: init.FS,
|
|
||||||
Options: init.SessionOptions,
|
|
||||||
}),
|
|
||||||
projects: make(map[Handle[project.Project]]tspath.Path),
|
|
||||||
files: make(handleMap[ast.SourceFile]),
|
|
||||||
symbols: make(handleMap[ast.Symbol]),
|
|
||||||
types: make(handleMap[checker.Type]),
|
|
||||||
}
|
|
||||||
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) HandleRequest(ctx context.Context, method string, payload []byte) ([]byte, error) {
|
|
||||||
params, err := unmarshalPayload(method, payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch Method(method) {
|
|
||||||
case MethodRelease:
|
|
||||||
if id, ok := params.(*string); ok {
|
|
||||||
return nil, api.releaseHandle(*id)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("expected string for release handle, got %T", params)
|
|
||||||
}
|
|
||||||
case MethodGetSourceFile:
|
|
||||||
params := params.(*GetSourceFileParams)
|
|
||||||
sourceFile, err := api.GetSourceFile(params.Project, params.FileName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return encoder.EncodeSourceFile(sourceFile, string(FileHandle(sourceFile)))
|
|
||||||
case MethodParseConfigFile:
|
|
||||||
return encodeJSON(api.ParseConfigFile(params.(*ParseConfigFileParams).FileName))
|
|
||||||
case MethodLoadProject:
|
|
||||||
return encodeJSON(api.LoadProject(ctx, params.(*LoadProjectParams).ConfigFileName))
|
|
||||||
case MethodGetSymbolAtPosition:
|
|
||||||
params := params.(*GetSymbolAtPositionParams)
|
|
||||||
return encodeJSON(api.GetSymbolAtPosition(ctx, params.Project, params.FileName, int(params.Position)))
|
|
||||||
case MethodGetSymbolsAtPositions:
|
|
||||||
params := params.(*GetSymbolsAtPositionsParams)
|
|
||||||
return encodeJSON(core.TryMap(params.Positions, func(position uint32) (any, error) {
|
|
||||||
return api.GetSymbolAtPosition(ctx, params.Project, params.FileName, int(position))
|
|
||||||
}))
|
|
||||||
case MethodGetSymbolAtLocation:
|
|
||||||
params := params.(*GetSymbolAtLocationParams)
|
|
||||||
return encodeJSON(api.GetSymbolAtLocation(ctx, params.Project, params.Location))
|
|
||||||
case MethodGetSymbolsAtLocations:
|
|
||||||
params := params.(*GetSymbolsAtLocationsParams)
|
|
||||||
return encodeJSON(core.TryMap(params.Locations, func(location Handle[ast.Node]) (any, error) {
|
|
||||||
return api.GetSymbolAtLocation(ctx, params.Project, location)
|
|
||||||
}))
|
|
||||||
case MethodGetTypeOfSymbol:
|
|
||||||
params := params.(*GetTypeOfSymbolParams)
|
|
||||||
return encodeJSON(api.GetTypeOfSymbol(ctx, params.Project, params.Symbol))
|
|
||||||
case MethodGetTypesOfSymbols:
|
|
||||||
params := params.(*GetTypesOfSymbolsParams)
|
|
||||||
return encodeJSON(core.TryMap(params.Symbols, func(symbol Handle[ast.Symbol]) (any, error) {
|
|
||||||
return api.GetTypeOfSymbol(ctx, params.Project, symbol)
|
|
||||||
}))
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unhandled API method %q", method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) Close() {
|
|
||||||
api.session.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) ParseConfigFile(configFileName string) (*ConfigFileResponse, error) {
|
|
||||||
configFileName = api.toAbsoluteFileName(configFileName)
|
|
||||||
configFileContent, ok := api.session.FS().ReadFile(configFileName)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("could not read file %q", configFileName)
|
|
||||||
}
|
|
||||||
configDir := tspath.GetDirectoryPath(configFileName)
|
|
||||||
tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, api.toPath(configFileName), configFileContent)
|
|
||||||
parsedCommandLine := tsoptions.ParseJsonSourceFileConfigFileContent(
|
|
||||||
tsConfigSourceFile,
|
|
||||||
api.session,
|
|
||||||
configDir,
|
|
||||||
nil, /*existingOptions*/
|
|
||||||
configFileName,
|
|
||||||
nil, /*resolutionStack*/
|
|
||||||
nil, /*extraFileExtensions*/
|
|
||||||
nil, /*extendedConfigCache*/
|
|
||||||
)
|
|
||||||
return &ConfigFileResponse{
|
|
||||||
FileNames: parsedCommandLine.FileNames(),
|
|
||||||
Options: parsedCommandLine.CompilerOptions(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) LoadProject(ctx context.Context, configFileName string) (*ProjectResponse, error) {
|
|
||||||
project, err := api.session.OpenProject(ctx, api.toAbsoluteFileName(configFileName))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := NewProjectResponse(project)
|
|
||||||
api.projects[data.Id] = project.ConfigFilePath()
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) GetSymbolAtPosition(ctx context.Context, projectId Handle[project.Project], fileName string, position int) (*SymbolResponse, error) {
|
|
||||||
projectPath, ok := api.projects[projectId]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("project ID not found")
|
|
||||||
}
|
|
||||||
snapshot, release := api.session.Snapshot()
|
|
||||||
defer release()
|
|
||||||
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
|
|
||||||
if project == nil {
|
|
||||||
return nil, errors.New("project not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
languageService := ls.NewLanguageService(project.GetProgram(), snapshot)
|
|
||||||
symbol, err := languageService.GetSymbolAtPosition(ctx, fileName, position)
|
|
||||||
if err != nil || symbol == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := NewSymbolResponse(symbol)
|
|
||||||
api.symbolsMu.Lock()
|
|
||||||
defer api.symbolsMu.Unlock()
|
|
||||||
api.symbols[data.Id] = symbol
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) GetSymbolAtLocation(ctx context.Context, projectId Handle[project.Project], location Handle[ast.Node]) (*SymbolResponse, error) {
|
|
||||||
projectPath, ok := api.projects[projectId]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("project ID not found")
|
|
||||||
}
|
|
||||||
snapshot, release := api.session.Snapshot()
|
|
||||||
defer release()
|
|
||||||
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
|
|
||||||
if project == nil {
|
|
||||||
return nil, errors.New("project not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
fileHandle, pos, kind, err := parseNodeHandle(location)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
api.filesMu.Lock()
|
|
||||||
defer api.filesMu.Unlock()
|
|
||||||
sourceFile, ok := api.files[fileHandle]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("file %q not found", fileHandle)
|
|
||||||
}
|
|
||||||
token := astnav.GetTokenAtPosition(sourceFile, pos)
|
|
||||||
if token == nil {
|
|
||||||
return nil, fmt.Errorf("token not found at position %d in file %q", pos, sourceFile.FileName())
|
|
||||||
}
|
|
||||||
node := ast.FindAncestorKind(token, kind)
|
|
||||||
if node == nil {
|
|
||||||
return nil, fmt.Errorf("node of kind %s not found at position %d in file %q", kind.String(), pos, sourceFile.FileName())
|
|
||||||
}
|
|
||||||
languageService := ls.NewLanguageService(project.GetProgram(), snapshot)
|
|
||||||
symbol := languageService.GetSymbolAtLocation(ctx, node)
|
|
||||||
if symbol == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
data := NewSymbolResponse(symbol)
|
|
||||||
api.symbolsMu.Lock()
|
|
||||||
defer api.symbolsMu.Unlock()
|
|
||||||
api.symbols[data.Id] = symbol
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) GetTypeOfSymbol(ctx context.Context, projectId Handle[project.Project], symbolHandle Handle[ast.Symbol]) (*TypeResponse, error) {
|
|
||||||
projectPath, ok := api.projects[projectId]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("project ID not found")
|
|
||||||
}
|
|
||||||
snapshot, release := api.session.Snapshot()
|
|
||||||
defer release()
|
|
||||||
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
|
|
||||||
if project == nil {
|
|
||||||
return nil, errors.New("project not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
api.symbolsMu.Lock()
|
|
||||||
defer api.symbolsMu.Unlock()
|
|
||||||
symbol, ok := api.symbols[symbolHandle]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("symbol %q not found", symbolHandle)
|
|
||||||
}
|
|
||||||
languageService := ls.NewLanguageService(project.GetProgram(), snapshot)
|
|
||||||
t := languageService.GetTypeOfSymbol(ctx, symbol)
|
|
||||||
if t == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return NewTypeData(t), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) GetSourceFile(projectId Handle[project.Project], fileName string) (*ast.SourceFile, error) {
|
|
||||||
projectPath, ok := api.projects[projectId]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("project ID not found")
|
|
||||||
}
|
|
||||||
snapshot, release := api.session.Snapshot()
|
|
||||||
defer release()
|
|
||||||
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
|
|
||||||
if project == nil {
|
|
||||||
return nil, errors.New("project not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceFile := project.GetProgram().GetSourceFile(fileName)
|
|
||||||
if sourceFile == nil {
|
|
||||||
return nil, fmt.Errorf("source file %q not found", fileName)
|
|
||||||
}
|
|
||||||
api.filesMu.Lock()
|
|
||||||
defer api.filesMu.Unlock()
|
|
||||||
api.files[FileHandle(sourceFile)] = sourceFile
|
|
||||||
return sourceFile, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) releaseHandle(handle string) error {
|
|
||||||
switch handle[0] {
|
|
||||||
case handlePrefixProject:
|
|
||||||
projectId := Handle[project.Project](handle)
|
|
||||||
_, ok := api.projects[projectId]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("project %q not found", handle)
|
|
||||||
}
|
|
||||||
delete(api.projects, projectId)
|
|
||||||
case handlePrefixFile:
|
|
||||||
fileId := Handle[ast.SourceFile](handle)
|
|
||||||
api.filesMu.Lock()
|
|
||||||
defer api.filesMu.Unlock()
|
|
||||||
_, ok := api.files[fileId]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("file %q not found", handle)
|
|
||||||
}
|
|
||||||
delete(api.files, fileId)
|
|
||||||
case handlePrefixSymbol:
|
|
||||||
symbolId := Handle[ast.Symbol](handle)
|
|
||||||
api.symbolsMu.Lock()
|
|
||||||
defer api.symbolsMu.Unlock()
|
|
||||||
_, ok := api.symbols[symbolId]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("symbol %q not found", handle)
|
|
||||||
}
|
|
||||||
delete(api.symbols, symbolId)
|
|
||||||
case handlePrefixType:
|
|
||||||
typeId := Handle[checker.Type](handle)
|
|
||||||
api.typesMu.Lock()
|
|
||||||
defer api.typesMu.Unlock()
|
|
||||||
_, ok := api.types[typeId]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("type %q not found", handle)
|
|
||||||
}
|
|
||||||
delete(api.types, typeId)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unhandled handle type %q", handle[0])
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) toAbsoluteFileName(fileName string) string {
|
|
||||||
return tspath.GetNormalizedAbsolutePath(fileName, api.session.GetCurrentDirectory())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) toPath(fileName string) tspath.Path {
|
|
||||||
return tspath.ToPath(fileName, api.session.GetCurrentDirectory(), api.session.FS().UseCaseSensitiveFileNames())
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeJSON(v any, err error) ([]byte, error) {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return json.Marshal(v)
|
|
||||||
}
|
|
||||||
@ -1,815 +0,0 @@
|
|||||||
package encoder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
NodeOffsetKind = iota * 4
|
|
||||||
NodeOffsetPos
|
|
||||||
NodeOffsetEnd
|
|
||||||
NodeOffsetNext
|
|
||||||
NodeOffsetParent
|
|
||||||
NodeOffsetData
|
|
||||||
// NodeSize is the number of bytes that represents a single node in the encoded format.
|
|
||||||
NodeSize
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
NodeDataTypeChildren uint32 = iota << 30
|
|
||||||
NodeDataTypeString
|
|
||||||
NodeDataTypeExtendedData
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
NodeDataTypeMask uint32 = 0xc0_00_00_00
|
|
||||||
NodeDataChildMask uint32 = 0x00_00_00_ff
|
|
||||||
NodeDataStringIndexMask uint32 = 0x00_ff_ff_ff
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
SyntaxKindNodeList uint32 = 1<<32 - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
HeaderOffsetMetadata = iota * 4
|
|
||||||
HeaderOffsetStringOffsets
|
|
||||||
HeaderOffsetStringData
|
|
||||||
HeaderOffsetExtendedData
|
|
||||||
HeaderOffsetNodes
|
|
||||||
HeaderSize
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ProtocolVersion uint8 = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// Source File Binary Format
|
|
||||||
// =========================
|
|
||||||
//
|
|
||||||
// The following defines a protocol for serializing TypeScript SourceFile objects to a compact binary format. All integer
|
|
||||||
// values are little-endian.
|
|
||||||
//
|
|
||||||
// Overview
|
|
||||||
// --------
|
|
||||||
//
|
|
||||||
// The format comprises six sections:
|
|
||||||
//
|
|
||||||
// | Section | Length | Description |
|
|
||||||
// | ------------------ | ------------------ | ---------------------------------------------------------------------------------------- |
|
|
||||||
// | Header | 20 bytes | Contains byte offsets to the start of each section. |
|
|
||||||
// | String offsets | 8 bytes per string | Pairs of starting byte offsets and ending byte offsets into the **string data** section. |
|
|
||||||
// | String data | variable | UTF-8 encoded string data. |
|
|
||||||
// | Extended node data | variable | Extra data for some kinds of nodes. |
|
|
||||||
// | Nodes | 24 bytes per node | Defines the AST structure of the file, with references to strings and extended data. |
|
|
||||||
//
|
|
||||||
// Header (20 bytes)
|
|
||||||
// -----------------
|
|
||||||
//
|
|
||||||
// The header contains the following fields:
|
|
||||||
//
|
|
||||||
// | Byte offset | Type | Field |
|
|
||||||
// | ----------- | ------ | ----------------------------------------- |
|
|
||||||
// | 0 | uint8 | Protocol version |
|
|
||||||
// | 1-4 | | Reserved |
|
|
||||||
// | 4-8 | uint32 | Byte offset to string offsets section |
|
|
||||||
// | 8-12 | uint32 | Byte offset to string data section |
|
|
||||||
// | 12-16 | uint32 | Byte offset to extended node data section |
|
|
||||||
// | 16-20 | uint32 | Byte offset to nodes section |
|
|
||||||
//
|
|
||||||
// String offsets (8 bytes per string)
|
|
||||||
// -----------------------------------
|
|
||||||
//
|
|
||||||
// Each string offset entry consists of two 4-byte unsigned integers, representing the start and end byte offsets into the
|
|
||||||
// **string data** section.
|
|
||||||
//
|
|
||||||
// String data (variable)
|
|
||||||
// ----------------------
|
|
||||||
//
|
|
||||||
// The string data section contains UTF-8 encoded string data. In typical cases, the entirety of the string data is the
|
|
||||||
// source file text, and individual nodes with string properties reference their positional slice of the file text. In
|
|
||||||
// cases where a node's string property is not equal to the slice of file text at its position, the unique string is
|
|
||||||
// appended to the string data section after the file text.
|
|
||||||
//
|
|
||||||
// Extended node data (variable)
|
|
||||||
// -----------------------------
|
|
||||||
//
|
|
||||||
// The extended node data section contains additional data for specific node types. The length and meaning of each entry
|
|
||||||
// is defined by the node type.
|
|
||||||
//
|
|
||||||
// Currently, the only node types that use this section are `TemplateHead`, `TemplateMiddle`, `TemplateTail`, and
|
|
||||||
// `SourceFile`. The extended data format for the first three is:
|
|
||||||
//
|
|
||||||
// | Byte offset | Type | Field |
|
|
||||||
// | ----------- | ------ | ------------------------------------------------ |
|
|
||||||
// | 0-4 | uint32 | Index of `text` in the string offsets section |
|
|
||||||
// | 4-8 | uint32 | Index of `rawText` in the string offsets section |
|
|
||||||
// | 8-12 | uint32 | Value of `templateFlags` |
|
|
||||||
//
|
|
||||||
// and for `SourceFile` is:
|
|
||||||
//
|
|
||||||
// | Byte offset | Type | Field |
|
|
||||||
// | ----------- | ------ | ------------------------------------------------- |
|
|
||||||
// | 0-4 | uint32 | Index of `text` in the string offsets section |
|
|
||||||
// | 4-8 | uint32 | Index of `fileName` in the string offsets section |
|
|
||||||
// | 8-12 | uint32 | Index of `id` in the string offsets section |
|
|
||||||
//
|
|
||||||
// Nodes (24 bytes per node)
|
|
||||||
// -------------------------
|
|
||||||
//
|
|
||||||
// The nodes section contains the AST structure of the file. Nodes are represented in a flat array in source order,
|
|
||||||
// heavily inspired by https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/. Each node has the following
|
|
||||||
// structure:
|
|
||||||
//
|
|
||||||
// | Byte offset | Type | Field |
|
|
||||||
// | ----------- | ------ | -------------------------- |
|
|
||||||
// | 0-4 | uint32 | Kind |
|
|
||||||
// | 4-8 | uint32 | Pos |
|
|
||||||
// | 8-12 | uint32 | End |
|
|
||||||
// | 12-16 | uint32 | Node index of next sibling |
|
|
||||||
// | 16-20 | uint32 | Node index of parent |
|
|
||||||
// | 20-24 | | Node data |
|
|
||||||
//
|
|
||||||
// The first 24 bytes of the nodes section are zeros representing a nil node, such that nodes without a parent or next
|
|
||||||
// sibling can unambiuously use `0` for those indices.
|
|
||||||
//
|
|
||||||
// NodeLists are represented as normal nodes with the special `kind` value `0xff_ff_ff_ff`. They are considered the parent
|
|
||||||
// of their contents in the encoded format. A client reconstructing an AST similar to TypeScript's internal representation
|
|
||||||
// should instead set the `parent` pointers of a NodeList's children to the NodeList's parent. A NodeList's `data` field
|
|
||||||
// is the uint32 length of the list, and does not use one of the data types described below.
|
|
||||||
//
|
|
||||||
// For node types other than NodeList, the node data field encodes one of the following, determined by the first 2 bits of
|
|
||||||
// the field:
|
|
||||||
//
|
|
||||||
// | Value | Data type | Description |
|
|
||||||
// | ----- | --------- | ------------------------------------------------------------------------------------ |
|
|
||||||
// | 0b00 | Children | Disambiguates which named properties of the node its children should be assigned to. |
|
|
||||||
// | 0b01 | String | The index of the node's string property in the **string offsets** section. |
|
|
||||||
// | 0b10 | Extended | The byte offset of the node's extended data into the **extended node data** section. |
|
|
||||||
// | 0b11 | Reserved | Reserved for future use. |
|
|
||||||
//
|
|
||||||
// In all node data types, the remaining 6 bits of the first byte are used to encode booleans specific to the node type:
|
|
||||||
//
|
|
||||||
// | Node type | Bits 2-5 | Bit 1 | Bit 0 |
|
|
||||||
// | ------------------------- | -------- | ------------- | ------------------------------- |
|
|
||||||
// | `ImportSpecifier` | | | `isTypeOnly` |
|
|
||||||
// | `ImportClause` | | | `isTypeOnly` |
|
|
||||||
// | `ExportSpecifier` | | | `isTypeOnly` |
|
|
||||||
// | `ImportEqualsDeclaration` | | | `isTypeOnly` |
|
|
||||||
// | `ExportDeclaration` | | | `isTypeOnly` |
|
|
||||||
// | `ImportTypeNode` | | | `isTypeOf` |
|
|
||||||
// | `ExportAssignment` | | | `isExportEquals` |
|
|
||||||
// | `Block` | | | `multiline` |
|
|
||||||
// | `ArrayLiteralExpression` | | | `multiline` |
|
|
||||||
// | `ObjectLiteralExpression` | | | `multiline` |
|
|
||||||
// | `JsxText` | | | `containsOnlyTriviaWhiteSpaces` |
|
|
||||||
// | `JSDocTypeLiteral` | | | `isArrayType` |
|
|
||||||
// | `JsDocPropertyTag` | | `isNameFirst` | `isBracketed` |
|
|
||||||
// | `JsDocParameterTag` | | `isNameFirst` | `isBracketed` |
|
|
||||||
// | `VariableDeclarationList` | | is `const` | is `let` |
|
|
||||||
// | `ImportAttributes` | | is `assert` | `multiline` |
|
|
||||||
//
|
|
||||||
// The remaining 3 bytes of the node data field vary by data type:
|
|
||||||
//
|
|
||||||
// ### Children (0b00)
|
|
||||||
//
|
|
||||||
// If a node has fewer children than its type allows, additional data is needed to determine which properties the children
|
|
||||||
// correspond to. The last byte of the 4-byte data field is a bitmask representing the child properties of the node type,
|
|
||||||
// in visitor order, where `1` indicates that the child at that property is present and `0` indicates that the property is
|
|
||||||
// nil. For example, a `MethodDeclaration` has the following child properties:
|
|
||||||
//
|
|
||||||
// | Property name | Bit position |
|
|
||||||
// | -------------- | ------------ |
|
|
||||||
// | modifiers | 0 |
|
|
||||||
// | asteriskToken | 1 |
|
|
||||||
// | name | 2 |
|
|
||||||
// | postfixToken | 3 |
|
|
||||||
// | typeParameters | 4 |
|
|
||||||
// | parameters | 5 |
|
|
||||||
// | returnType | 6 |
|
|
||||||
// | body | 7 |
|
|
||||||
//
|
|
||||||
// A bitmask with value `0b01100101` would indicate that the next four direct descendants (i.e., node records that have a
|
|
||||||
// `parent` set to the node index of the `MethodDeclaration`) of the node are its `modifiers`, `name`, `parameters`, and
|
|
||||||
// `body` properties, in that order. The remaining properties are nil. (To reconstruct the node with named properties, the
|
|
||||||
// client must consult a static table of each node type's child property names.)
|
|
||||||
//
|
|
||||||
// The bitmask may be zero for node types that can only have a single child, since no disambiguation is needed.
|
|
||||||
// Additionally, the children data type may be used for nodes that can never have children, but do not require other
|
|
||||||
// data types.
|
|
||||||
//
|
|
||||||
// ### String (0b01)
|
|
||||||
//
|
|
||||||
// The string data type is used for nodes with a single string property. (Currently, the name of that property is always
|
|
||||||
// `text`.) The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e.,
|
|
||||||
// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is an index into the **string offsets** section. The *N*th 32-bit
|
|
||||||
// unsigned integer in the **string offsets** section is the byte offset of the start of the string in the **string data**
|
|
||||||
// section, and the *N+1*th 32-bit unsigned integer is the byte offset of the end of the string in the
|
|
||||||
// **string data** section.
|
|
||||||
//
|
|
||||||
// ### Extended (0b10)
|
|
||||||
//
|
|
||||||
// The extended data type is used for nodes with properties that don't fit into either the children or string data types.
|
|
||||||
// The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e.,
|
|
||||||
// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is a byte offset into the **extended node data** section. The length and
|
|
||||||
// meaning of the data at that offset is defined by the node type. See the **Extended node data** section for details on
|
|
||||||
// the format of the extended data for specific node types.
|
|
||||||
|
|
||||||
func EncodeSourceFile(sourceFile *ast.SourceFile, id string) ([]byte, error) {
|
|
||||||
var parentIndex, nodeCount, prevIndex uint32
|
|
||||||
var extendedData []byte
|
|
||||||
strs := newStringTable(sourceFile.Text(), sourceFile.TextCount)
|
|
||||||
nodes := make([]byte, 0, (sourceFile.NodeCount+1)*NodeSize)
|
|
||||||
|
|
||||||
visitor := &ast.NodeVisitor{
|
|
||||||
Hooks: ast.NodeVisitorHooks{
|
|
||||||
VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
|
|
||||||
if nodeList == nil || len(nodeList.Nodes) == 0 {
|
|
||||||
return nodeList
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeCount++
|
|
||||||
if prevIndex != 0 {
|
|
||||||
// this is the next sibling of `prevNode`
|
|
||||||
b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24)
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = appendUint32s(nodes, SyntaxKindNodeList, uint32(nodeList.Pos()), uint32(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)))
|
|
||||||
|
|
||||||
saveParentIndex := parentIndex
|
|
||||||
|
|
||||||
currentIndex := nodeCount
|
|
||||||
prevIndex = 0
|
|
||||||
parentIndex = currentIndex
|
|
||||||
visitor.VisitSlice(nodeList.Nodes)
|
|
||||||
prevIndex = currentIndex
|
|
||||||
parentIndex = saveParentIndex
|
|
||||||
|
|
||||||
return nodeList
|
|
||||||
},
|
|
||||||
VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList {
|
|
||||||
if modifiers != nil && len(modifiers.Nodes) > 0 {
|
|
||||||
visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor)
|
|
||||||
}
|
|
||||||
return modifiers
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
visitor.Visit = func(node *ast.Node) *ast.Node {
|
|
||||||
nodeCount++
|
|
||||||
if prevIndex != 0 {
|
|
||||||
// this is the next sibling of `prevNode`
|
|
||||||
b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24)
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2
|
|
||||||
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = appendUint32s(nodes, uint32(node.Kind), uint32(node.Pos()), uint32(node.End()), 0, parentIndex, getNodeData(node, strs, &extendedData))
|
|
||||||
|
|
||||||
saveParentIndex := parentIndex
|
|
||||||
|
|
||||||
currentIndex := nodeCount
|
|
||||||
prevIndex = 0
|
|
||||||
parentIndex = currentIndex
|
|
||||||
visitor.VisitEachChild(node)
|
|
||||||
prevIndex = currentIndex
|
|
||||||
parentIndex = saveParentIndex
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = appendUint32s(nodes, 0, 0, 0, 0, 0, 0)
|
|
||||||
|
|
||||||
nodeCount++
|
|
||||||
parentIndex++
|
|
||||||
nodes = appendUint32s(nodes, uint32(sourceFile.Kind), uint32(sourceFile.Pos()), uint32(sourceFile.End()), 0, 0, getSourceFileData(sourceFile, id, strs, &extendedData))
|
|
||||||
|
|
||||||
visitor.VisitEachChild(sourceFile.AsNode())
|
|
||||||
|
|
||||||
metadata := uint32(ProtocolVersion) << 24
|
|
||||||
offsetStringTableOffsets := HeaderSize
|
|
||||||
offsetStringTableData := HeaderSize + len(strs.offsets)*4
|
|
||||||
offsetExtendedData := offsetStringTableData + strs.stringLength()
|
|
||||||
offsetNodes := offsetExtendedData + len(extendedData)
|
|
||||||
|
|
||||||
header := []uint32{
|
|
||||||
metadata,
|
|
||||||
uint32(offsetStringTableOffsets),
|
|
||||||
uint32(offsetStringTableData),
|
|
||||||
uint32(offsetExtendedData),
|
|
||||||
uint32(offsetNodes),
|
|
||||||
}
|
|
||||||
|
|
||||||
var headerBytes, strsBytes []byte
|
|
||||||
headerBytes = appendUint32s(nil, header...)
|
|
||||||
strsBytes = strs.encode()
|
|
||||||
|
|
||||||
return slices.Concat(
|
|
||||||
headerBytes,
|
|
||||||
strsBytes,
|
|
||||||
extendedData,
|
|
||||||
nodes,
|
|
||||||
), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendUint32s(buf []byte, values ...uint32) []byte {
|
|
||||||
for _, value := range values {
|
|
||||||
var err error
|
|
||||||
if buf, err = binary.Append(buf, binary.LittleEndian, value); err != nil {
|
|
||||||
// The only error binary.Append can return is for values that are not fixed-size.
|
|
||||||
// This can never happen here, since we are always appending uint32.
|
|
||||||
panic(fmt.Sprintf("failed to append uint32: %v", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSourceFileData(sourceFile *ast.SourceFile, id string, strs *stringTable, extendedData *[]byte) uint32 {
|
|
||||||
t := NodeDataTypeExtendedData
|
|
||||||
extendedDataOffset := len(*extendedData)
|
|
||||||
textIndex := strs.add(sourceFile.Text(), sourceFile.Kind, sourceFile.Pos(), sourceFile.End())
|
|
||||||
fileNameIndex := strs.add(sourceFile.FileName(), 0, 0, 0)
|
|
||||||
idIndex := strs.add(id, 0, 0, 0)
|
|
||||||
*extendedData = appendUint32s(*extendedData, textIndex, fileNameIndex, idIndex)
|
|
||||||
return t | uint32(extendedDataOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNodeData(node *ast.Node, strs *stringTable, extendedData *[]byte) uint32 {
|
|
||||||
t := getNodeDataType(node)
|
|
||||||
switch t {
|
|
||||||
case NodeDataTypeChildren:
|
|
||||||
return t | getNodeDefinedData(node) | uint32(getChildrenPropertyMask(node))
|
|
||||||
case NodeDataTypeString:
|
|
||||||
return t | getNodeDefinedData(node) | recordNodeStrings(node, strs)
|
|
||||||
case NodeDataTypeExtendedData:
|
|
||||||
return t | getNodeDefinedData(node) | recordExtendedData(node, strs, extendedData)
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNodeDataType(node *ast.Node) uint32 {
|
|
||||||
switch node.Kind {
|
|
||||||
case ast.KindJsxText,
|
|
||||||
ast.KindIdentifier,
|
|
||||||
ast.KindPrivateIdentifier,
|
|
||||||
ast.KindStringLiteral,
|
|
||||||
ast.KindNumericLiteral,
|
|
||||||
ast.KindBigIntLiteral,
|
|
||||||
ast.KindRegularExpressionLiteral,
|
|
||||||
ast.KindNoSubstitutionTemplateLiteral,
|
|
||||||
ast.KindJSDocText:
|
|
||||||
return NodeDataTypeString
|
|
||||||
case ast.KindTemplateHead,
|
|
||||||
ast.KindTemplateMiddle,
|
|
||||||
ast.KindTemplateTail,
|
|
||||||
ast.KindSourceFile:
|
|
||||||
return NodeDataTypeExtendedData
|
|
||||||
default:
|
|
||||||
return NodeDataTypeChildren
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getChildrenPropertyMask returns a mask of which children properties are present in the node.
|
|
||||||
// It is defined for node kinds that have more than one property that is a pointer to a child node.
|
|
||||||
// Example: QualifiedName has two children properties: Left and Right, which are visited in that order.
|
|
||||||
// result&1 is non-zero if Left is present, and result&2 is non-zero if Right is present. If the client
|
|
||||||
// knows that QualifiedName has properties ["Left", "Right"] and sees an encoded node with only one
|
|
||||||
// child, it can use the mask to determine which property is present.
|
|
||||||
func getChildrenPropertyMask(node *ast.Node) uint8 {
|
|
||||||
switch node.Kind {
|
|
||||||
case ast.KindQualifiedName:
|
|
||||||
n := node.AsQualifiedName()
|
|
||||||
return (boolToByte(n.Left != nil) << 0) | (boolToByte(n.Right != nil) << 1)
|
|
||||||
case ast.KindTypeParameter:
|
|
||||||
n := node.AsTypeParameter()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Constraint != nil) << 2) | (boolToByte(n.DefaultType != nil) << 3)
|
|
||||||
case ast.KindIfStatement:
|
|
||||||
n := node.AsIfStatement()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThenStatement != nil) << 1) | (boolToByte(n.ElseStatement != nil) << 2)
|
|
||||||
case ast.KindDoStatement:
|
|
||||||
n := node.AsDoStatement()
|
|
||||||
return (boolToByte(n.Statement != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
|
||||||
case ast.KindWhileStatement:
|
|
||||||
n := node.AsWhileStatement()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
|
|
||||||
case ast.KindForStatement:
|
|
||||||
n := node.AsForStatement()
|
|
||||||
return (boolToByte(n.Initializer != nil) << 0) | (boolToByte(n.Condition != nil) << 1) | (boolToByte(n.Incrementor != nil) << 2) | (boolToByte(n.Statement != nil) << 3)
|
|
||||||
case ast.KindForInStatement, ast.KindForOfStatement:
|
|
||||||
n := node.AsForInOrOfStatement()
|
|
||||||
return (boolToByte(n.AwaitModifier != nil) << 0) | (boolToByte(n.Initializer != nil) << 1) | (boolToByte(n.Expression != nil) << 2) | (boolToByte(n.Statement != nil) << 3)
|
|
||||||
case ast.KindWithStatement:
|
|
||||||
n := node.AsWithStatement()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
|
|
||||||
case ast.KindSwitchStatement:
|
|
||||||
n := node.AsSwitchStatement()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.CaseBlock != nil) << 1)
|
|
||||||
case ast.KindCaseClause, ast.KindDefaultClause:
|
|
||||||
n := node.AsCaseOrDefaultClause()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statements != nil) << 1)
|
|
||||||
case ast.KindTryStatement:
|
|
||||||
n := node.AsTryStatement()
|
|
||||||
return (boolToByte(n.TryBlock != nil) << 0) | (boolToByte(n.CatchClause != nil) << 1) | (boolToByte(n.FinallyBlock != nil) << 2)
|
|
||||||
case ast.KindCatchClause:
|
|
||||||
n := node.AsCatchClause()
|
|
||||||
return (boolToByte(n.VariableDeclaration != nil) << 0) | (boolToByte(n.Block != nil) << 1)
|
|
||||||
case ast.KindLabeledStatement:
|
|
||||||
n := node.AsLabeledStatement()
|
|
||||||
return (boolToByte(n.Label != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
|
|
||||||
case ast.KindVariableStatement:
|
|
||||||
n := node.AsVariableStatement()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.DeclarationList != nil) << 1)
|
|
||||||
case ast.KindVariableDeclaration:
|
|
||||||
n := node.AsVariableDeclaration()
|
|
||||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.ExclamationToken != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
|
|
||||||
case ast.KindParameter:
|
|
||||||
n := node.AsParameterDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.DotDotDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Initializer != nil) << 5)
|
|
||||||
case ast.KindBindingElement:
|
|
||||||
n := node.AsBindingElement()
|
|
||||||
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.PropertyName != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
|
|
||||||
case ast.KindFunctionDeclaration:
|
|
||||||
n := node.AsFunctionDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6)
|
|
||||||
case ast.KindInterfaceDeclaration:
|
|
||||||
n := node.AsInterfaceDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
|
|
||||||
case ast.KindTypeAliasDeclaration:
|
|
||||||
n := node.AsTypeAliasDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Type != nil) << 3)
|
|
||||||
case ast.KindEnumMember:
|
|
||||||
n := node.AsEnumMember()
|
|
||||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1)
|
|
||||||
case ast.KindEnumDeclaration:
|
|
||||||
n := node.AsEnumDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Members != nil) << 2)
|
|
||||||
case ast.KindModuleDeclaration:
|
|
||||||
n := node.AsModuleDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Body != nil) << 2)
|
|
||||||
case ast.KindImportEqualsDeclaration:
|
|
||||||
n := node.AsImportEqualsDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.ModuleReference != nil) << 2)
|
|
||||||
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
|
|
||||||
n := node.AsImportDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
|
|
||||||
case ast.KindImportSpecifier:
|
|
||||||
n := node.AsImportSpecifier()
|
|
||||||
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
|
|
||||||
case ast.KindImportClause:
|
|
||||||
n := node.AsImportClause()
|
|
||||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.NamedBindings != nil) << 1)
|
|
||||||
case ast.KindExportAssignment, ast.KindJSExportAssignment:
|
|
||||||
n := node.AsExportAssignment()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
|
||||||
case ast.KindNamespaceExportDeclaration:
|
|
||||||
n := node.AsNamespaceExportDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
|
|
||||||
case ast.KindExportDeclaration:
|
|
||||||
n := node.AsExportDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.ExportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
|
|
||||||
case ast.KindExportSpecifier:
|
|
||||||
n := node.AsExportSpecifier()
|
|
||||||
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
|
|
||||||
case ast.KindCallSignature:
|
|
||||||
n := node.AsCallSignatureDeclaration()
|
|
||||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
|
||||||
case ast.KindConstructSignature:
|
|
||||||
n := node.AsConstructSignatureDeclaration()
|
|
||||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
|
||||||
case ast.KindConstructor:
|
|
||||||
n := node.AsConstructorDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Body != nil) << 4)
|
|
||||||
case ast.KindGetAccessor:
|
|
||||||
n := node.AsGetAccessorDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5)
|
|
||||||
case ast.KindSetAccessor:
|
|
||||||
n := node.AsSetAccessorDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5)
|
|
||||||
case ast.KindIndexSignature:
|
|
||||||
n := node.AsIndexSignatureDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
|
||||||
case ast.KindMethodSignature:
|
|
||||||
n := node.AsMethodSignatureDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5)
|
|
||||||
case ast.KindMethodDeclaration:
|
|
||||||
n := node.AsMethodDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.PostfixToken != nil) << 3) | (boolToByte(n.TypeParameters != nil) << 4) | (boolToByte(n.Parameters != nil) << 5) | (boolToByte(n.Type != nil) << 6) | (boolToByte(n.Body != nil) << 7)
|
|
||||||
case ast.KindPropertySignature:
|
|
||||||
n := node.AsPropertySignatureDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
|
|
||||||
case ast.KindPropertyDeclaration:
|
|
||||||
n := node.AsPropertyDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
|
|
||||||
case ast.KindBinaryExpression:
|
|
||||||
n := node.AsBinaryExpression()
|
|
||||||
return (boolToByte(n.Left != nil) << 0) | (boolToByte(n.OperatorToken != nil) << 1) | (boolToByte(n.Right != nil) << 2)
|
|
||||||
case ast.KindYieldExpression:
|
|
||||||
n := node.AsYieldExpression()
|
|
||||||
return (boolToByte(n.AsteriskToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
|
||||||
case ast.KindArrowFunction:
|
|
||||||
n := node.AsArrowFunction()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsGreaterThanToken != nil) << 4) | (boolToByte(n.Body != nil) << 5)
|
|
||||||
case ast.KindFunctionExpression:
|
|
||||||
n := node.AsFunctionExpression()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6)
|
|
||||||
case ast.KindAsExpression:
|
|
||||||
n := node.AsAsExpression()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1)
|
|
||||||
case ast.KindSatisfiesExpression:
|
|
||||||
n := node.AsSatisfiesExpression()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1)
|
|
||||||
case ast.KindConditionalExpression:
|
|
||||||
n := node.AsConditionalExpression()
|
|
||||||
return (boolToByte(n.Condition != nil) << 0) | (boolToByte(n.QuestionToken != nil) << 1) | (boolToByte(n.WhenTrue != nil) << 2) | (boolToByte(n.ColonToken != nil) << 3) | (boolToByte(n.WhenFalse != nil) << 4)
|
|
||||||
case ast.KindPropertyAccessExpression:
|
|
||||||
n := node.AsPropertyAccessExpression()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2)
|
|
||||||
case ast.KindElementAccessExpression:
|
|
||||||
n := node.AsElementAccessExpression()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.ArgumentExpression != nil) << 2)
|
|
||||||
case ast.KindCallExpression:
|
|
||||||
n := node.AsCallExpression()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Arguments != nil) << 3)
|
|
||||||
case ast.KindNewExpression:
|
|
||||||
n := node.AsNewExpression()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Arguments != nil) << 2)
|
|
||||||
case ast.KindTemplateExpression:
|
|
||||||
n := node.AsTemplateExpression()
|
|
||||||
return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1)
|
|
||||||
case ast.KindTemplateSpan:
|
|
||||||
n := node.AsTemplateSpan()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Literal != nil) << 1)
|
|
||||||
case ast.KindTaggedTemplateExpression:
|
|
||||||
n := node.AsTaggedTemplateExpression()
|
|
||||||
return (boolToByte(n.Tag != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Template != nil) << 3)
|
|
||||||
case ast.KindPropertyAssignment:
|
|
||||||
n := node.AsPropertyAssignment()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
|
|
||||||
case ast.KindShorthandPropertyAssignment:
|
|
||||||
n := node.AsShorthandPropertyAssignment()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.EqualsToken != nil) << 3) | (boolToByte(n.ObjectAssignmentInitializer != nil) << 4)
|
|
||||||
case ast.KindTypeAssertionExpression:
|
|
||||||
n := node.AsTypeAssertion()
|
|
||||||
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
|
||||||
case ast.KindConditionalType:
|
|
||||||
n := node.AsConditionalTypeNode()
|
|
||||||
return (boolToByte(n.CheckType != nil) << 0) | (boolToByte(n.ExtendsType != nil) << 1) | (boolToByte(n.TrueType != nil) << 2) | (boolToByte(n.FalseType != nil) << 3)
|
|
||||||
case ast.KindIndexedAccessType:
|
|
||||||
n := node.AsIndexedAccessTypeNode()
|
|
||||||
return (boolToByte(n.ObjectType != nil) << 0) | (boolToByte(n.IndexType != nil) << 1)
|
|
||||||
case ast.KindTypeReference:
|
|
||||||
n := node.AsTypeReferenceNode()
|
|
||||||
return (boolToByte(n.TypeName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
|
|
||||||
case ast.KindExpressionWithTypeArguments:
|
|
||||||
n := node.AsExpressionWithTypeArguments()
|
|
||||||
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
|
|
||||||
case ast.KindTypePredicate:
|
|
||||||
n := node.AsTypePredicateNode()
|
|
||||||
return (boolToByte(n.AssertsModifier != nil) << 0) | (boolToByte(n.ParameterName != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
|
||||||
case ast.KindImportType:
|
|
||||||
n := node.AsImportTypeNode()
|
|
||||||
return (boolToByte(n.Argument != nil) << 0) | (boolToByte(n.Attributes != nil) << 1) | (boolToByte(n.Qualifier != nil) << 2) | (boolToByte(n.TypeArguments != nil) << 3)
|
|
||||||
case ast.KindImportAttribute:
|
|
||||||
n := node.AsImportAttribute()
|
|
||||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Value != nil) << 1)
|
|
||||||
case ast.KindTypeQuery:
|
|
||||||
n := node.AsTypeQueryNode()
|
|
||||||
return (boolToByte(n.ExprName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
|
|
||||||
case ast.KindMappedType:
|
|
||||||
n := node.AsMappedTypeNode()
|
|
||||||
return (boolToByte(n.ReadonlyToken != nil) << 0) | (boolToByte(n.TypeParameter != nil) << 1) | (boolToByte(n.NameType != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Members != nil) << 5)
|
|
||||||
case ast.KindNamedTupleMember:
|
|
||||||
n := node.AsNamedTupleMember()
|
|
||||||
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.QuestionToken != nil) << 2) | (boolToByte(n.Type != nil) << 3)
|
|
||||||
case ast.KindFunctionType:
|
|
||||||
n := node.AsFunctionTypeNode()
|
|
||||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
|
||||||
case ast.KindConstructorType:
|
|
||||||
n := node.AsConstructorTypeNode()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3)
|
|
||||||
case ast.KindTemplateLiteralType:
|
|
||||||
n := node.AsTemplateLiteralTypeNode()
|
|
||||||
return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1)
|
|
||||||
case ast.KindTemplateLiteralTypeSpan:
|
|
||||||
n := node.AsTemplateLiteralTypeSpan()
|
|
||||||
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Literal != nil) << 1)
|
|
||||||
case ast.KindJsxElement:
|
|
||||||
n := node.AsJsxElement()
|
|
||||||
return (boolToByte(n.OpeningElement != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingElement != nil) << 2)
|
|
||||||
case ast.KindJsxNamespacedName:
|
|
||||||
n := node.AsJsxNamespacedName()
|
|
||||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Namespace != nil) << 1)
|
|
||||||
case ast.KindJsxOpeningElement:
|
|
||||||
n := node.AsJsxOpeningElement()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2)
|
|
||||||
case ast.KindJsxSelfClosingElement:
|
|
||||||
n := node.AsJsxSelfClosingElement()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2)
|
|
||||||
case ast.KindJsxFragment:
|
|
||||||
n := node.AsJsxFragment()
|
|
||||||
return (boolToByte(n.OpeningFragment != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingFragment != nil) << 2)
|
|
||||||
case ast.KindJsxAttribute:
|
|
||||||
n := node.AsJsxAttribute()
|
|
||||||
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1)
|
|
||||||
case ast.KindJsxExpression:
|
|
||||||
n := node.AsJsxExpression()
|
|
||||||
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
|
|
||||||
case ast.KindJSDoc:
|
|
||||||
n := node.AsJSDoc()
|
|
||||||
return (boolToByte(n.Comment != nil) << 0) | (boolToByte(n.Tags != nil) << 1)
|
|
||||||
case ast.KindJSDocTypeTag:
|
|
||||||
n := node.AsJSDocTypeTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocTag:
|
|
||||||
n := node.AsJSDocUnknownTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocTemplateTag:
|
|
||||||
n := node.AsJSDocTemplateTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Constraint != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
|
||||||
case ast.KindJSDocReturnTag:
|
|
||||||
n := node.AsJSDocReturnTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocPublicTag:
|
|
||||||
n := node.AsJSDocPublicTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocPrivateTag:
|
|
||||||
n := node.AsJSDocPrivateTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocProtectedTag:
|
|
||||||
n := node.AsJSDocProtectedTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocReadonlyTag:
|
|
||||||
n := node.AsJSDocReadonlyTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocOverrideTag:
|
|
||||||
n := node.AsJSDocOverrideTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocDeprecatedTag:
|
|
||||||
n := node.AsJSDocDeprecatedTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
|
|
||||||
case ast.KindJSDocSeeTag:
|
|
||||||
n := node.AsJSDocSeeTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.NameExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocImplementsTag:
|
|
||||||
n := node.AsJSDocImplementsTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocAugmentsTag:
|
|
||||||
n := node.AsJSDocAugmentsTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocSatisfiesTag:
|
|
||||||
n := node.AsJSDocSatisfiesTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocThisTag:
|
|
||||||
n := node.AsJSDocThisTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocImportTag:
|
|
||||||
n := node.AsJSDocImportTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3) | (boolToByte(n.Comment != nil) << 4)
|
|
||||||
case ast.KindJSDocCallbackTag:
|
|
||||||
n := node.AsJSDocCallbackTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.FullName != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
|
||||||
case ast.KindJSDocOverloadTag:
|
|
||||||
n := node.AsJSDocOverloadTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
|
|
||||||
case ast.KindJSDocTypedefTag:
|
|
||||||
n := node.AsJSDocTypedefTag()
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
|
||||||
case ast.KindJSDocSignature:
|
|
||||||
n := node.AsJSDocSignature()
|
|
||||||
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
|
|
||||||
case ast.KindClassStaticBlockDeclaration:
|
|
||||||
n := node.AsClassStaticBlockDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Body != nil) << 1)
|
|
||||||
case ast.KindClassDeclaration:
|
|
||||||
n := node.AsClassDeclaration()
|
|
||||||
return (boolToByte(n.Modifiers() != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
|
|
||||||
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
|
|
||||||
n := node.AsJSDocParameterOrPropertyTag()
|
|
||||||
if n.IsNameFirst {
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeExpression != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
|
||||||
}
|
|
||||||
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNodeDefinedData(node *ast.Node) uint32 {
|
|
||||||
switch node.Kind {
|
|
||||||
case ast.KindJSDocTypeLiteral:
|
|
||||||
n := node.AsJSDocTypeLiteral()
|
|
||||||
return uint32(boolToByte(n.IsArrayType)) << 24
|
|
||||||
case ast.KindImportSpecifier:
|
|
||||||
n := node.AsImportSpecifier()
|
|
||||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
|
||||||
case ast.KindImportClause:
|
|
||||||
n := node.AsImportClause()
|
|
||||||
return uint32(boolToByte(n.PhaseModifier == ast.KindTypeKeyword))<<24 | uint32(boolToByte(n.PhaseModifier == ast.KindDeferKeyword))<<25
|
|
||||||
case ast.KindExportSpecifier:
|
|
||||||
n := node.AsExportSpecifier()
|
|
||||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
|
||||||
case ast.KindImportType:
|
|
||||||
n := node.AsImportTypeNode()
|
|
||||||
return uint32(boolToByte(n.IsTypeOf)) << 24
|
|
||||||
case ast.KindImportEqualsDeclaration:
|
|
||||||
n := node.AsImportEqualsDeclaration()
|
|
||||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
|
||||||
case ast.KindExportAssignment:
|
|
||||||
n := node.AsExportAssignment()
|
|
||||||
return uint32(boolToByte(n.IsExportEquals)) << 24
|
|
||||||
case ast.KindExportDeclaration:
|
|
||||||
n := node.AsExportDeclaration()
|
|
||||||
return uint32(boolToByte(n.IsTypeOnly)) << 24
|
|
||||||
case ast.KindBlock:
|
|
||||||
n := node.AsBlock()
|
|
||||||
return uint32(boolToByte(n.Multiline)) << 24
|
|
||||||
case ast.KindArrayLiteralExpression:
|
|
||||||
n := node.AsArrayLiteralExpression()
|
|
||||||
return uint32(boolToByte(n.MultiLine)) << 24
|
|
||||||
case ast.KindObjectLiteralExpression:
|
|
||||||
n := node.AsObjectLiteralExpression()
|
|
||||||
return uint32(boolToByte(n.MultiLine)) << 24
|
|
||||||
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
|
|
||||||
n := node.AsJSDocParameterOrPropertyTag()
|
|
||||||
return uint32(boolToByte(n.IsBracketed))<<24 | uint32(boolToByte(n.IsNameFirst))<<25
|
|
||||||
case ast.KindJsxText:
|
|
||||||
n := node.AsJsxText()
|
|
||||||
return uint32(boolToByte(n.ContainsOnlyTriviaWhiteSpaces)) << 24
|
|
||||||
case ast.KindVariableDeclarationList:
|
|
||||||
n := node.AsVariableDeclarationList()
|
|
||||||
return uint32(n.Flags & (ast.NodeFlagsLet | ast.NodeFlagsConst) << 24)
|
|
||||||
case ast.KindImportAttributes:
|
|
||||||
n := node.AsImportAttributes()
|
|
||||||
return uint32(boolToByte(n.MultiLine))<<24 | uint32(boolToByte(n.Token == ast.KindAssertKeyword))<<25
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {
|
|
||||||
switch node.Kind {
|
|
||||||
case ast.KindJsxText:
|
|
||||||
return strs.add(node.AsJsxText().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindIdentifier:
|
|
||||||
return strs.add(node.AsIdentifier().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindPrivateIdentifier:
|
|
||||||
return strs.add(node.AsPrivateIdentifier().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindStringLiteral:
|
|
||||||
return strs.add(node.AsStringLiteral().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindNumericLiteral:
|
|
||||||
return strs.add(node.AsNumericLiteral().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindBigIntLiteral:
|
|
||||||
return strs.add(node.AsBigIntLiteral().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindRegularExpressionLiteral:
|
|
||||||
return strs.add(node.AsRegularExpressionLiteral().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindNoSubstitutionTemplateLiteral:
|
|
||||||
return strs.add(node.AsNoSubstitutionTemplateLiteral().Text, node.Kind, node.Pos(), node.End())
|
|
||||||
case ast.KindJSDocText:
|
|
||||||
return strs.add(node.AsJSDocText().Text(), node.Kind, node.Pos(), node.End())
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordExtendedData(node *ast.Node, strs *stringTable, extendedData *[]byte) uint32 {
|
|
||||||
offset := uint32(len(*extendedData))
|
|
||||||
var text, rawText string
|
|
||||||
var templateFlags uint32
|
|
||||||
switch node.Kind {
|
|
||||||
case ast.KindTemplateTail:
|
|
||||||
n := node.AsTemplateTail()
|
|
||||||
text = n.Text
|
|
||||||
rawText = n.RawText
|
|
||||||
templateFlags = uint32(n.TemplateFlags)
|
|
||||||
case ast.KindTemplateMiddle:
|
|
||||||
n := node.AsTemplateMiddle()
|
|
||||||
text = n.Text
|
|
||||||
rawText = n.RawText
|
|
||||||
templateFlags = uint32(n.TemplateFlags)
|
|
||||||
case ast.KindTemplateHead:
|
|
||||||
n := node.AsTemplateHead()
|
|
||||||
text = n.Text
|
|
||||||
rawText = n.RawText
|
|
||||||
templateFlags = uint32(n.TemplateFlags)
|
|
||||||
}
|
|
||||||
textIndex := strs.add(text, node.Kind, node.Pos(), node.End())
|
|
||||||
rawTextIndex := strs.add(rawText, node.Kind, node.Pos(), node.End())
|
|
||||||
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, templateFlags)
|
|
||||||
return offset
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolToByte(b bool) byte {
|
|
||||||
if b {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
package encoder_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/api/encoder"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/baseline"
|
|
||||||
"gotest.tools/v3/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeSourceFile(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
|
||||||
FileName: "/test.ts",
|
|
||||||
Path: "/test.ts",
|
|
||||||
}, "import { bar } from \"bar\";\nexport function foo<T, U>(a: string, b: string): any {}\nfoo();", core.ScriptKindTS)
|
|
||||||
t.Run("baseline", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
buf, err := encoder.EncodeSourceFile(sourceFile, "")
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
str := formatEncodedSourceFile(buf)
|
|
||||||
baseline.Run(t, "encodeSourceFile.txt", str, baseline.Options{
|
|
||||||
Subfolder: "api",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncodeSourceFile(b *testing.B) {
|
|
||||||
repo.SkipIfNoTypeScriptSubmodule(b)
|
|
||||||
filePath := filepath.Join(repo.TypeScriptSubmodulePath, "src/compiler/checker.ts")
|
|
||||||
fileContent, err := os.ReadFile(filePath)
|
|
||||||
assert.NilError(b, err)
|
|
||||||
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
|
||||||
FileName: "/checker.ts",
|
|
||||||
Path: "/checker.ts",
|
|
||||||
}, string(fileContent), core.ScriptKindTS)
|
|
||||||
|
|
||||||
for b.Loop() {
|
|
||||||
_, err := encoder.EncodeSourceFile(sourceFile, "")
|
|
||||||
assert.NilError(b, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readUint32(buf []byte, offset int) uint32 {
|
|
||||||
return binary.LittleEndian.Uint32(buf[offset : offset+4])
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatEncodedSourceFile(encoded []byte) string {
|
|
||||||
var result strings.Builder
|
|
||||||
var getIndent func(parentIndex uint32) string
|
|
||||||
offsetNodes := readUint32(encoded, encoder.HeaderOffsetNodes)
|
|
||||||
offsetStringOffsets := readUint32(encoded, encoder.HeaderOffsetStringOffsets)
|
|
||||||
offsetStrings := readUint32(encoded, encoder.HeaderOffsetStringData)
|
|
||||||
getIndent = func(parentIndex uint32) string {
|
|
||||||
if parentIndex == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return " " + getIndent(readUint32(encoded, int(offsetNodes)+int(parentIndex)*encoder.NodeSize+encoder.NodeOffsetParent))
|
|
||||||
}
|
|
||||||
j := 1
|
|
||||||
for i := int(offsetNodes) + encoder.NodeSize; i < len(encoded); i += encoder.NodeSize {
|
|
||||||
kind := readUint32(encoded, i+encoder.NodeOffsetKind)
|
|
||||||
pos := readUint32(encoded, i+encoder.NodeOffsetPos)
|
|
||||||
end := readUint32(encoded, i+encoder.NodeOffsetEnd)
|
|
||||||
parentIndex := readUint32(encoded, i+encoder.NodeOffsetParent)
|
|
||||||
result.WriteString(getIndent(parentIndex))
|
|
||||||
if kind == encoder.SyntaxKindNodeList {
|
|
||||||
result.WriteString("NodeList")
|
|
||||||
} else {
|
|
||||||
result.WriteString(ast.Kind(kind).String())
|
|
||||||
}
|
|
||||||
if ast.Kind(kind) == ast.KindIdentifier || ast.Kind(kind) == ast.KindStringLiteral {
|
|
||||||
stringIndex := readUint32(encoded, i+encoder.NodeOffsetData) & encoder.NodeDataStringIndexMask
|
|
||||||
strStart := readUint32(encoded, int(offsetStringOffsets+stringIndex*4))
|
|
||||||
strEnd := readUint32(encoded, int(offsetStringOffsets+stringIndex*4)+4)
|
|
||||||
str := string(encoded[offsetStrings+strStart : offsetStrings+strEnd])
|
|
||||||
result.WriteString(fmt.Sprintf(" \"%s\"", str))
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&result, " [%d, %d), i=%d, next=%d", pos, end, j, encoded[i+encoder.NodeOffsetNext])
|
|
||||||
result.WriteString("\n")
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
return result.String()
|
|
||||||
}
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
package encoder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stringTable struct {
|
|
||||||
fileText string
|
|
||||||
otherStrings *strings.Builder
|
|
||||||
// offsets are pos/end pairs
|
|
||||||
offsets []uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStringTable(fileText string, stringCount int) *stringTable {
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
return &stringTable{
|
|
||||||
fileText: fileText,
|
|
||||||
otherStrings: builder,
|
|
||||||
offsets: make([]uint32, 0, stringCount*2),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *stringTable) add(text string, kind ast.Kind, pos int, end int) uint32 {
|
|
||||||
index := uint32(len(t.offsets))
|
|
||||||
if kind == ast.KindSourceFile {
|
|
||||||
t.offsets = append(t.offsets, uint32(pos), uint32(end))
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
length := len(text)
|
|
||||||
if end-pos > 0 {
|
|
||||||
// pos includes leading trivia, but we can usually infer the actual start of the
|
|
||||||
// string from the kind and end
|
|
||||||
endOffset := 0
|
|
||||||
if kind == ast.KindStringLiteral || kind == ast.KindTemplateTail || kind == ast.KindNoSubstitutionTemplateLiteral {
|
|
||||||
endOffset = 1
|
|
||||||
}
|
|
||||||
end = end - endOffset
|
|
||||||
start := end - length
|
|
||||||
fileSlice := t.fileText[start:end]
|
|
||||||
if fileSlice == text {
|
|
||||||
t.offsets = append(t.offsets, uint32(start), uint32(end))
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no exact match, so we need to add it to the string table
|
|
||||||
offset := len(t.fileText) + t.otherStrings.Len()
|
|
||||||
t.otherStrings.WriteString(text)
|
|
||||||
t.offsets = append(t.offsets, uint32(offset), uint32(offset+length))
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *stringTable) encode() []byte {
|
|
||||||
result := make([]byte, 0, t.encodedLength())
|
|
||||||
result = appendUint32s(result, t.offsets...)
|
|
||||||
result = append(result, t.fileText...)
|
|
||||||
result = append(result, t.otherStrings.String()...)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *stringTable) stringLength() int {
|
|
||||||
return len(t.fileText) + t.otherStrings.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *stringTable) encodedLength() int {
|
|
||||||
return len(t.offsets)*4 + len(t.fileText) + t.otherStrings.Len()
|
|
||||||
}
|
|
||||||
@ -1,218 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/project"
|
|
||||||
"github.com/go-json-experiment/json"
|
|
||||||
"github.com/go-json-experiment/json/jsontext"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidRequest = errors.New("api: invalid request")
|
|
||||||
ErrClientError = errors.New("api: client error")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Method string
|
|
||||||
|
|
||||||
type Handle[T any] string
|
|
||||||
|
|
||||||
const (
|
|
||||||
handlePrefixProject = 'p'
|
|
||||||
handlePrefixSymbol = 's'
|
|
||||||
handlePrefixType = 't'
|
|
||||||
handlePrefixFile = 'f'
|
|
||||||
handlePrefixNode = 'n'
|
|
||||||
)
|
|
||||||
|
|
||||||
func ProjectHandle(p *project.Project) Handle[project.Project] {
|
|
||||||
return createHandle[project.Project](handlePrefixProject, p.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
func SymbolHandle(symbol *ast.Symbol) Handle[ast.Symbol] {
|
|
||||||
return createHandle[ast.Symbol](handlePrefixSymbol, ast.GetSymbolId(symbol))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TypeHandle(t *checker.Type) Handle[checker.Type] {
|
|
||||||
return createHandle[checker.Type](handlePrefixType, t.Id())
|
|
||||||
}
|
|
||||||
|
|
||||||
func FileHandle(file *ast.SourceFile) Handle[ast.SourceFile] {
|
|
||||||
return createHandle[ast.SourceFile](handlePrefixFile, ast.GetNodeId(file.AsNode()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func NodeHandle(node *ast.Node) Handle[ast.Node] {
|
|
||||||
fileHandle := FileHandle(ast.GetSourceFileOfNode(node))
|
|
||||||
return Handle[ast.Node](fmt.Sprintf("%s.%d.%d", fileHandle, node.Pos(), node.Kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseNodeHandle(handle Handle[ast.Node]) (Handle[ast.SourceFile], int, ast.Kind, error) {
|
|
||||||
parts := strings.SplitN(string(handle), ".", 3)
|
|
||||||
if len(parts) != 3 {
|
|
||||||
return "", 0, 0, fmt.Errorf("invalid node handle %q", handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileHandle := Handle[ast.SourceFile](parts[0])
|
|
||||||
pos, err := strconv.ParseInt(parts[1], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, 0, fmt.Errorf("invalid node handle %q: %w", handle, err)
|
|
||||||
}
|
|
||||||
kind, err := strconv.ParseInt(parts[2], 10, 16)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, 0, fmt.Errorf("invalid node handle %q: %w", handle, err)
|
|
||||||
}
|
|
||||||
return fileHandle, int(pos), ast.Kind(kind), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createHandle[T any](prefix rune, id any) Handle[T] {
|
|
||||||
return Handle[T](fmt.Sprintf("%c%016x", prefix, id))
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
MethodConfigure Method = "configure"
|
|
||||||
MethodRelease Method = "release"
|
|
||||||
|
|
||||||
MethodParseConfigFile Method = "parseConfigFile"
|
|
||||||
MethodLoadProject Method = "loadProject"
|
|
||||||
MethodGetSymbolAtPosition Method = "getSymbolAtPosition"
|
|
||||||
MethodGetSymbolsAtPositions Method = "getSymbolsAtPositions"
|
|
||||||
MethodGetSymbolAtLocation Method = "getSymbolAtLocation"
|
|
||||||
MethodGetSymbolsAtLocations Method = "getSymbolsAtLocations"
|
|
||||||
MethodGetTypeOfSymbol Method = "getTypeOfSymbol"
|
|
||||||
MethodGetTypesOfSymbols Method = "getTypesOfSymbols"
|
|
||||||
MethodGetSourceFile Method = "getSourceFile"
|
|
||||||
)
|
|
||||||
|
|
||||||
var unmarshalers = map[Method]func([]byte) (any, error){
|
|
||||||
MethodRelease: unmarshallerFor[string],
|
|
||||||
MethodParseConfigFile: unmarshallerFor[ParseConfigFileParams],
|
|
||||||
MethodLoadProject: unmarshallerFor[LoadProjectParams],
|
|
||||||
MethodGetSourceFile: unmarshallerFor[GetSourceFileParams],
|
|
||||||
MethodGetSymbolAtPosition: unmarshallerFor[GetSymbolAtPositionParams],
|
|
||||||
MethodGetSymbolsAtPositions: unmarshallerFor[GetSymbolsAtPositionsParams],
|
|
||||||
MethodGetSymbolAtLocation: unmarshallerFor[GetSymbolAtLocationParams],
|
|
||||||
MethodGetSymbolsAtLocations: unmarshallerFor[GetSymbolsAtLocationsParams],
|
|
||||||
MethodGetTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams],
|
|
||||||
MethodGetTypesOfSymbols: unmarshallerFor[GetTypesOfSymbolsParams],
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigureParams struct {
|
|
||||||
Callbacks []string `json:"callbacks"`
|
|
||||||
LogFile string `json:"logFile"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParseConfigFileParams struct {
|
|
||||||
FileName string `json:"fileName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigFileResponse struct {
|
|
||||||
FileNames []string `json:"fileNames"`
|
|
||||||
Options *core.CompilerOptions `json:"options"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoadProjectParams struct {
|
|
||||||
ConfigFileName string `json:"configFileName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProjectResponse struct {
|
|
||||||
Id Handle[project.Project] `json:"id"`
|
|
||||||
ConfigFileName string `json:"configFileName"`
|
|
||||||
RootFiles []string `json:"rootFiles"`
|
|
||||||
CompilerOptions *core.CompilerOptions `json:"compilerOptions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProjectResponse(project *project.Project) *ProjectResponse {
|
|
||||||
return &ProjectResponse{
|
|
||||||
Id: ProjectHandle(project),
|
|
||||||
ConfigFileName: project.Name(),
|
|
||||||
RootFiles: project.CommandLine.FileNames(),
|
|
||||||
CompilerOptions: project.CommandLine.CompilerOptions(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetSymbolAtPositionParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
FileName string `json:"fileName"`
|
|
||||||
Position uint32 `json:"position"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetSymbolsAtPositionsParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
FileName string `json:"fileName"`
|
|
||||||
Positions []uint32 `json:"positions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetSymbolAtLocationParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
Location Handle[ast.Node] `json:"location"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetSymbolsAtLocationsParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
Locations []Handle[ast.Node] `json:"locations"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SymbolResponse struct {
|
|
||||||
Id Handle[ast.Symbol] `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Flags uint32 `json:"flags"`
|
|
||||||
CheckFlags uint32 `json:"checkFlags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSymbolResponse(symbol *ast.Symbol) *SymbolResponse {
|
|
||||||
return &SymbolResponse{
|
|
||||||
Id: SymbolHandle(symbol),
|
|
||||||
Name: symbol.Name,
|
|
||||||
Flags: uint32(symbol.Flags),
|
|
||||||
CheckFlags: uint32(symbol.CheckFlags),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetTypeOfSymbolParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
Symbol Handle[ast.Symbol] `json:"symbol"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetTypesOfSymbolsParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
Symbols []Handle[ast.Symbol] `json:"symbols"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TypeResponse struct {
|
|
||||||
Id Handle[checker.Type] `json:"id"`
|
|
||||||
Flags uint32 `json:"flags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTypeData(t *checker.Type) *TypeResponse {
|
|
||||||
return &TypeResponse{
|
|
||||||
Id: TypeHandle(t),
|
|
||||||
Flags: uint32(t.Flags()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetSourceFileParams struct {
|
|
||||||
Project Handle[project.Project] `json:"project"`
|
|
||||||
FileName string `json:"fileName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalPayload(method string, payload jsontext.Value) (any, error) {
|
|
||||||
unmarshaler, ok := unmarshalers[Method(method)]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown API method %q", method)
|
|
||||||
}
|
|
||||||
return unmarshaler(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshallerFor[T any](data []byte) (any, error) {
|
|
||||||
var v T
|
|
||||||
if err := json.Unmarshal(data, &v); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to unmarshal %T: %w", (*T)(nil), err)
|
|
||||||
}
|
|
||||||
return &v, nil
|
|
||||||
}
|
|
||||||
@ -1,497 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"runtime/debug"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/bundled"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/lsp/lsproto"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/project"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/project/logging"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs"
|
|
||||||
"github.com/go-json-experiment/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate go tool golang.org/x/tools/cmd/stringer -type=MessageType -output=stringer_generated.go
|
|
||||||
//go:generate go tool mvdan.cc/gofumpt -w stringer_generated.go
|
|
||||||
|
|
||||||
type MessageType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
MessageTypeUnknown MessageType = iota
|
|
||||||
MessageTypeRequest
|
|
||||||
MessageTypeCallResponse
|
|
||||||
MessageTypeCallError
|
|
||||||
MessageTypeResponse
|
|
||||||
MessageTypeError
|
|
||||||
MessageTypeCall
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m MessageType) IsValid() bool {
|
|
||||||
return m >= MessageTypeRequest && m <= MessageTypeCall
|
|
||||||
}
|
|
||||||
|
|
||||||
type MessagePackType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
MessagePackTypeFixedArray3 MessagePackType = 0x93
|
|
||||||
MessagePackTypeBin8 MessagePackType = 0xC4
|
|
||||||
MessagePackTypeBin16 MessagePackType = 0xC5
|
|
||||||
MessagePackTypeBin32 MessagePackType = 0xC6
|
|
||||||
MessagePackTypeU8 MessagePackType = 0xCC
|
|
||||||
)
|
|
||||||
|
|
||||||
type Callback int
|
|
||||||
|
|
||||||
const (
|
|
||||||
CallbackDirectoryExists Callback = 1 << iota
|
|
||||||
CallbackFileExists
|
|
||||||
CallbackGetAccessibleEntries
|
|
||||||
CallbackReadFile
|
|
||||||
CallbackRealpath
|
|
||||||
)
|
|
||||||
|
|
||||||
type ServerOptions struct {
|
|
||||||
In io.Reader
|
|
||||||
Out io.Writer
|
|
||||||
Err io.Writer
|
|
||||||
Cwd string
|
|
||||||
DefaultLibraryPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ vfs.FS = (*Server)(nil)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
r *bufio.Reader
|
|
||||||
w *bufio.Writer
|
|
||||||
stderr io.Writer
|
|
||||||
|
|
||||||
cwd string
|
|
||||||
newLine string
|
|
||||||
fs vfs.FS
|
|
||||||
defaultLibraryPath string
|
|
||||||
|
|
||||||
callbackMu sync.Mutex
|
|
||||||
enabledCallbacks Callback
|
|
||||||
logger logging.Logger
|
|
||||||
api *API
|
|
||||||
|
|
||||||
requestId int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer(options *ServerOptions) *Server {
|
|
||||||
if options.Cwd == "" {
|
|
||||||
panic("Cwd is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &Server{
|
|
||||||
r: bufio.NewReader(options.In),
|
|
||||||
w: bufio.NewWriter(options.Out),
|
|
||||||
stderr: options.Err,
|
|
||||||
cwd: options.Cwd,
|
|
||||||
fs: bundled.WrapFS(osvfs.FS()),
|
|
||||||
defaultLibraryPath: options.DefaultLibraryPath,
|
|
||||||
}
|
|
||||||
logger := logging.NewLogger(options.Err)
|
|
||||||
server.logger = logger
|
|
||||||
server.api = NewAPI(&APIInit{
|
|
||||||
Logger: logger,
|
|
||||||
FS: server,
|
|
||||||
SessionOptions: &project.SessionOptions{
|
|
||||||
CurrentDirectory: options.Cwd,
|
|
||||||
DefaultLibraryPath: options.DefaultLibraryPath,
|
|
||||||
PositionEncoding: lsproto.PositionEncodingKindUTF8,
|
|
||||||
LoggingEnabled: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultLibraryPath implements APIHost.
|
|
||||||
func (s *Server) DefaultLibraryPath() string {
|
|
||||||
return s.defaultLibraryPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// FS implements APIHost.
|
|
||||||
func (s *Server) FS() vfs.FS {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCurrentDirectory implements APIHost.
|
|
||||||
func (s *Server) GetCurrentDirectory() string {
|
|
||||||
return s.cwd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Run() error {
|
|
||||||
for {
|
|
||||||
messageType, method, payload, err := s.readRequest("")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch messageType {
|
|
||||||
case MessageTypeRequest:
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
stack := debug.Stack()
|
|
||||||
err = fmt.Errorf("panic handling request: %v\n%s", r, string(stack))
|
|
||||||
if fatalErr := s.sendError(method, err); fatalErr != nil {
|
|
||||||
panic("fatal error sending panic response")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
result, err := s.handleRequest(method, payload)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if err := s.sendError(method, err); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := s.sendResponse(method, result); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("%w: expected request, received: %s", ErrInvalidRequest, messageType.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) readRequest(expectedMethod string) (messageType MessageType, method string, payload []byte, err error) {
|
|
||||||
t, err := s.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return messageType, method, payload, err
|
|
||||||
}
|
|
||||||
if MessagePackType(t) != MessagePackTypeFixedArray3 {
|
|
||||||
return messageType, method, payload, fmt.Errorf("%w: expected message to be encoded as fixed 3-element array (0x93), received: 0x%2x", ErrInvalidRequest, t)
|
|
||||||
}
|
|
||||||
t, err = s.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return messageType, method, payload, err
|
|
||||||
}
|
|
||||||
if MessagePackType(t) != MessagePackTypeU8 {
|
|
||||||
return messageType, method, payload, fmt.Errorf("%w: expected first element of message tuple to be encoded as unsigned 8-bit int (0xcc), received: 0x%2x", ErrInvalidRequest, t)
|
|
||||||
}
|
|
||||||
rawMessageType, err := s.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return messageType, method, payload, err
|
|
||||||
}
|
|
||||||
messageType = MessageType(rawMessageType)
|
|
||||||
if !messageType.IsValid() {
|
|
||||||
return messageType, method, payload, fmt.Errorf("%w: unknown message type: %d", ErrInvalidRequest, messageType)
|
|
||||||
}
|
|
||||||
rawMethod, err := s.readBin()
|
|
||||||
if err != nil {
|
|
||||||
return messageType, method, payload, err
|
|
||||||
}
|
|
||||||
method = string(rawMethod)
|
|
||||||
if expectedMethod != "" && method != expectedMethod {
|
|
||||||
return messageType, method, payload, fmt.Errorf("%w: expected method %q, received %q", ErrInvalidRequest, expectedMethod, method)
|
|
||||||
}
|
|
||||||
payload, err = s.readBin()
|
|
||||||
return messageType, method, payload, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) readBin() ([]byte, error) {
|
|
||||||
// https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family
|
|
||||||
t, err := s.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var size uint
|
|
||||||
switch MessagePackType(t) {
|
|
||||||
case MessagePackTypeBin8:
|
|
||||||
var size8 uint8
|
|
||||||
if err = binary.Read(s.r, binary.BigEndian, &size8); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size = uint(size8)
|
|
||||||
case MessagePackTypeBin16:
|
|
||||||
var size16 uint16
|
|
||||||
if err = binary.Read(s.r, binary.BigEndian, &size16); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size = uint(size16)
|
|
||||||
case MessagePackTypeBin32:
|
|
||||||
var size32 uint32
|
|
||||||
if err = binary.Read(s.r, binary.BigEndian, &size32); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size = uint(size32)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("%w: expected binary data length (0xc4-0xc6), received: 0x%2x", ErrInvalidRequest, t)
|
|
||||||
}
|
|
||||||
payload := make([]byte, size)
|
|
||||||
bytesRead, err := io.ReadFull(s.r, payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if bytesRead != int(size) {
|
|
||||||
return nil, fmt.Errorf("%w: expected %d bytes, read %d", ErrInvalidRequest, size, bytesRead)
|
|
||||||
}
|
|
||||||
return payload, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) enableCallback(callback string) error {
|
|
||||||
switch callback {
|
|
||||||
case "directoryExists":
|
|
||||||
s.enabledCallbacks |= CallbackDirectoryExists
|
|
||||||
case "fileExists":
|
|
||||||
s.enabledCallbacks |= CallbackFileExists
|
|
||||||
case "getAccessibleEntries":
|
|
||||||
s.enabledCallbacks |= CallbackGetAccessibleEntries
|
|
||||||
case "readFile":
|
|
||||||
s.enabledCallbacks |= CallbackReadFile
|
|
||||||
case "realpath":
|
|
||||||
s.enabledCallbacks |= CallbackRealpath
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown callback: %s", callback)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleRequest(method string, payload []byte) ([]byte, error) {
|
|
||||||
s.requestId++
|
|
||||||
switch method {
|
|
||||||
case "configure":
|
|
||||||
return nil, s.handleConfigure(payload)
|
|
||||||
case "echo":
|
|
||||||
return payload, nil
|
|
||||||
default:
|
|
||||||
return s.api.HandleRequest(core.WithRequestID(context.Background(), strconv.Itoa(s.requestId)), method, payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleConfigure(payload []byte) error {
|
|
||||||
var params *ConfigureParams
|
|
||||||
if err := json.Unmarshal(payload, ¶ms); err != nil {
|
|
||||||
return fmt.Errorf("%w: %w", ErrInvalidRequest, err)
|
|
||||||
}
|
|
||||||
for _, callback := range params.Callbacks {
|
|
||||||
if err := s.enableCallback(callback); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// !!!
|
|
||||||
if params.LogFile != "" {
|
|
||||||
// s.logger.SetFile(params.LogFile)
|
|
||||||
} else {
|
|
||||||
// s.logger.SetFile("")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) sendResponse(method string, result []byte) error {
|
|
||||||
return s.writeMessage(MessageTypeResponse, method, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) sendError(method string, err error) error {
|
|
||||||
return s.writeMessage(MessageTypeError, method, []byte(err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) writeMessage(messageType MessageType, method string, payload []byte) error {
|
|
||||||
if err := s.w.WriteByte(byte(MessagePackTypeFixedArray3)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.w.WriteByte(byte(MessagePackTypeU8)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.w.WriteByte(byte(messageType)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.writeBin([]byte(method)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.writeBin(payload); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.w.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) writeBin(payload []byte) error {
|
|
||||||
length := len(payload)
|
|
||||||
if length < 256 {
|
|
||||||
if err := s.w.WriteByte(byte(MessagePackTypeBin8)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.w.WriteByte(byte(length)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if length < 1<<16 {
|
|
||||||
if err := s.w.WriteByte(byte(MessagePackTypeBin16)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := binary.Write(s.w, binary.BigEndian, uint16(length)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := s.w.WriteByte(byte(MessagePackTypeBin32)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := binary.Write(s.w, binary.BigEndian, uint32(length)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := s.w.Write(payload)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) call(method string, payload any) ([]byte, error) {
|
|
||||||
s.callbackMu.Lock()
|
|
||||||
defer s.callbackMu.Unlock()
|
|
||||||
jsonPayload, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = s.writeMessage(MessageTypeCall, method, jsonPayload); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
messageType, _, responsePayload, err := s.readRequest(method)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageType != MessageTypeCallResponse && messageType != MessageTypeCallError {
|
|
||||||
return nil, fmt.Errorf("%w: expected call-response or call-error, received: %s", ErrInvalidRequest, messageType.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageType == MessageTypeCallError {
|
|
||||||
return nil, fmt.Errorf("%w: %s", ErrClientError, responsePayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
return responsePayload, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectoryExists implements vfs.FS.
|
|
||||||
func (s *Server) DirectoryExists(path string) bool {
|
|
||||||
if s.enabledCallbacks&CallbackDirectoryExists != 0 {
|
|
||||||
result, err := s.call("directoryExists", path)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(result) > 0 {
|
|
||||||
return string(result) == "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.fs.DirectoryExists(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileExists implements vfs.FS.
|
|
||||||
func (s *Server) FileExists(path string) bool {
|
|
||||||
if s.enabledCallbacks&CallbackFileExists != 0 {
|
|
||||||
result, err := s.call("fileExists", path)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(result) > 0 {
|
|
||||||
return string(result) == "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.fs.FileExists(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAccessibleEntries implements vfs.FS.
|
|
||||||
func (s *Server) GetAccessibleEntries(path string) vfs.Entries {
|
|
||||||
if s.enabledCallbacks&CallbackGetAccessibleEntries != 0 {
|
|
||||||
result, err := s.call("getAccessibleEntries", path)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(result) > 0 {
|
|
||||||
var rawEntries *struct {
|
|
||||||
Files []string `json:"files"`
|
|
||||||
Directories []string `json:"directories"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(result, &rawEntries); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if rawEntries != nil {
|
|
||||||
return vfs.Entries{
|
|
||||||
Files: rawEntries.Files,
|
|
||||||
Directories: rawEntries.Directories,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.fs.GetAccessibleEntries(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFile implements vfs.FS.
|
|
||||||
func (s *Server) ReadFile(path string) (contents string, ok bool) {
|
|
||||||
if s.enabledCallbacks&CallbackReadFile != 0 {
|
|
||||||
data, err := s.call("readFile", path)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if string(data) == "null" {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if len(data) > 0 {
|
|
||||||
var result string
|
|
||||||
if err := json.Unmarshal(data, &result); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return result, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.fs.ReadFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Realpath implements vfs.FS.
|
|
||||||
func (s *Server) Realpath(path string) string {
|
|
||||||
if s.enabledCallbacks&CallbackRealpath != 0 {
|
|
||||||
data, err := s.call("realpath", path)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(data) > 0 {
|
|
||||||
var result string
|
|
||||||
if err := json.Unmarshal(data, &result); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.fs.Realpath(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseCaseSensitiveFileNames implements vfs.FS.
|
|
||||||
func (s *Server) UseCaseSensitiveFileNames() bool {
|
|
||||||
return s.fs.UseCaseSensitiveFileNames()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFile implements vfs.FS.
|
|
||||||
func (s *Server) WriteFile(path string, data string, writeByteOrderMark bool) error {
|
|
||||||
return s.fs.WriteFile(path, data, writeByteOrderMark)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalkDir implements vfs.FS.
|
|
||||||
func (s *Server) WalkDir(root string, walkFn vfs.WalkDirFunc) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat implements vfs.FS.
|
|
||||||
func (s *Server) Stat(path string) vfs.FileInfo {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove implements vfs.FS.
|
|
||||||
func (s *Server) Remove(path string) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chtimes implements vfs.FS.
|
|
||||||
func (s *Server) Chtimes(path string, aTime time.Time, mTime time.Time) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// Code generated by "stringer -type=MessageType -output=stringer_generated.go"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[MessageTypeUnknown-0]
|
|
||||||
_ = x[MessageTypeRequest-1]
|
|
||||||
_ = x[MessageTypeCallResponse-2]
|
|
||||||
_ = x[MessageTypeCallError-3]
|
|
||||||
_ = x[MessageTypeResponse-4]
|
|
||||||
_ = x[MessageTypeError-5]
|
|
||||||
_ = x[MessageTypeCall-6]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _MessageType_name = "MessageTypeUnknownMessageTypeRequestMessageTypeCallResponseMessageTypeCallErrorMessageTypeResponseMessageTypeErrorMessageTypeCall"
|
|
||||||
|
|
||||||
var _MessageType_index = [...]uint8{0, 18, 36, 59, 79, 98, 114, 129}
|
|
||||||
|
|
||||||
func (i MessageType) String() string {
|
|
||||||
if i >= MessageType(len(_MessageType_index)-1) {
|
|
||||||
return "MessageType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _MessageType_name[_MessageType_index[i]:_MessageType_index[i+1]]
|
|
||||||
}
|
|
||||||
@ -1,418 +0,0 @@
|
|||||||
package diagnosticwriter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"maps"
|
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FormattingOptions struct {
|
|
||||||
tspath.ComparePathsOptions
|
|
||||||
NewLine string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
foregroundColorEscapeGrey = "\u001b[90m"
|
|
||||||
foregroundColorEscapeRed = "\u001b[91m"
|
|
||||||
foregroundColorEscapeYellow = "\u001b[93m"
|
|
||||||
foregroundColorEscapeBlue = "\u001b[94m"
|
|
||||||
foregroundColorEscapeCyan = "\u001b[96m"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
gutterStyleSequence = "\u001b[7m"
|
|
||||||
gutterSeparator = " "
|
|
||||||
resetEscapeSequence = "\u001b[0m"
|
|
||||||
ellipsis = "..."
|
|
||||||
)
|
|
||||||
|
|
||||||
func FormatDiagnosticsWithColorAndContext(output io.Writer, diags []*ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
if len(diags) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i, diagnostic := range diags {
|
|
||||||
if i > 0 {
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
FormatDiagnosticWithColorAndContext(output, diagnostic, formatOpts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatDiagnosticWithColorAndContext(output io.Writer, diagnostic *ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
if diagnostic.File() != nil {
|
|
||||||
file := diagnostic.File()
|
|
||||||
pos := diagnostic.Loc().Pos()
|
|
||||||
WriteLocation(output, file, pos, formatOpts, writeWithStyleAndReset)
|
|
||||||
fmt.Fprint(output, " - ")
|
|
||||||
}
|
|
||||||
|
|
||||||
writeWithStyleAndReset(output, diagnostic.Category().Name(), getCategoryFormat(diagnostic.Category()))
|
|
||||||
fmt.Fprintf(output, "%s TS%d: %s", foregroundColorEscapeGrey, diagnostic.Code(), resetEscapeSequence)
|
|
||||||
WriteFlattenedDiagnosticMessage(output, diagnostic, formatOpts.NewLine)
|
|
||||||
|
|
||||||
if diagnostic.File() != nil && diagnostic.Code() != diagnostics.File_appears_to_be_binary.Code() {
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
writeCodeSnippet(output, diagnostic.File(), diagnostic.Pos(), diagnostic.Len(), getCategoryFormat(diagnostic.Category()), "", formatOpts)
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diagnostic.RelatedInformation() != nil) && (len(diagnostic.RelatedInformation()) > 0) {
|
|
||||||
for _, relatedInformation := range diagnostic.RelatedInformation() {
|
|
||||||
file := relatedInformation.File()
|
|
||||||
if file != nil {
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
fmt.Fprint(output, " ")
|
|
||||||
pos := relatedInformation.Pos()
|
|
||||||
WriteLocation(output, file, pos, formatOpts, writeWithStyleAndReset)
|
|
||||||
fmt.Fprint(output, " - ")
|
|
||||||
WriteFlattenedDiagnosticMessage(output, relatedInformation, formatOpts.NewLine)
|
|
||||||
writeCodeSnippet(output, file, pos, relatedInformation.Len(), foregroundColorEscapeCyan, " ", formatOpts)
|
|
||||||
}
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeCodeSnippet(writer io.Writer, sourceFile *ast.SourceFile, start int, length int, squiggleColor string, indent string, formatOpts *FormattingOptions) {
|
|
||||||
firstLine, firstLineChar := scanner.GetECMALineAndCharacterOfPosition(sourceFile, start)
|
|
||||||
lastLine, lastLineChar := scanner.GetECMALineAndCharacterOfPosition(sourceFile, start+length)
|
|
||||||
if length == 0 {
|
|
||||||
lastLineChar++ // When length is zero, squiggle the character right after the start position.
|
|
||||||
}
|
|
||||||
|
|
||||||
lastLineOfFile, _ := scanner.GetECMALineAndCharacterOfPosition(sourceFile, len(sourceFile.Text()))
|
|
||||||
|
|
||||||
hasMoreThanFiveLines := lastLine-firstLine >= 4
|
|
||||||
gutterWidth := len(strconv.Itoa(lastLine + 1))
|
|
||||||
if hasMoreThanFiveLines {
|
|
||||||
gutterWidth = max(len(ellipsis), gutterWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := firstLine; i <= lastLine; i++ {
|
|
||||||
fmt.Fprint(writer, formatOpts.NewLine)
|
|
||||||
|
|
||||||
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
|
|
||||||
// so we'll skip ahead to the second-to-last line.
|
|
||||||
if hasMoreThanFiveLines && firstLine+1 < i && i < lastLine-1 {
|
|
||||||
fmt.Fprint(writer, indent)
|
|
||||||
fmt.Fprint(writer, gutterStyleSequence)
|
|
||||||
fmt.Fprintf(writer, "%*s", gutterWidth, ellipsis)
|
|
||||||
fmt.Fprint(writer, resetEscapeSequence)
|
|
||||||
fmt.Fprint(writer, gutterSeparator)
|
|
||||||
fmt.Fprint(writer, formatOpts.NewLine)
|
|
||||||
i = lastLine - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
lineStart := scanner.GetECMAPositionOfLineAndCharacter(sourceFile, i, 0)
|
|
||||||
var lineEnd int
|
|
||||||
if i < lastLineOfFile {
|
|
||||||
lineEnd = scanner.GetECMAPositionOfLineAndCharacter(sourceFile, i+1, 0)
|
|
||||||
} else {
|
|
||||||
lineEnd = sourceFile.Loc.End()
|
|
||||||
}
|
|
||||||
|
|
||||||
lineContent := strings.TrimRightFunc(sourceFile.Text()[lineStart:lineEnd], unicode.IsSpace) // trim from end
|
|
||||||
lineContent = strings.ReplaceAll(lineContent, "\t", " ") // convert tabs to single spaces
|
|
||||||
|
|
||||||
// Output the gutter and the actual contents of the line.
|
|
||||||
fmt.Fprint(writer, indent)
|
|
||||||
fmt.Fprint(writer, gutterStyleSequence)
|
|
||||||
fmt.Fprintf(writer, "%*d", gutterWidth, i+1)
|
|
||||||
fmt.Fprint(writer, resetEscapeSequence)
|
|
||||||
fmt.Fprint(writer, gutterSeparator)
|
|
||||||
fmt.Fprint(writer, lineContent)
|
|
||||||
fmt.Fprint(writer, formatOpts.NewLine)
|
|
||||||
|
|
||||||
// Output the gutter and the error span for the line using tildes.
|
|
||||||
fmt.Fprint(writer, indent)
|
|
||||||
fmt.Fprint(writer, gutterStyleSequence)
|
|
||||||
fmt.Fprintf(writer, "%*s", gutterWidth, "")
|
|
||||||
fmt.Fprint(writer, resetEscapeSequence)
|
|
||||||
fmt.Fprint(writer, gutterSeparator)
|
|
||||||
fmt.Fprint(writer, squiggleColor)
|
|
||||||
switch i {
|
|
||||||
case firstLine:
|
|
||||||
// If we're on the last line, then limit it to the last character of the last line.
|
|
||||||
// Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
|
|
||||||
var lastCharForLine int
|
|
||||||
if i == lastLine {
|
|
||||||
lastCharForLine = lastLineChar
|
|
||||||
} else {
|
|
||||||
lastCharForLine = len(lineContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill with spaces until the first character,
|
|
||||||
// then squiggle the remainder of the line.
|
|
||||||
fmt.Fprint(writer, strings.Repeat(" ", firstLineChar))
|
|
||||||
fmt.Fprint(writer, strings.Repeat("~", lastCharForLine-firstLineChar))
|
|
||||||
case lastLine:
|
|
||||||
// Squiggle until the final character.
|
|
||||||
fmt.Fprint(writer, strings.Repeat("~", lastLineChar))
|
|
||||||
default:
|
|
||||||
// Squiggle the entire line.
|
|
||||||
fmt.Fprint(writer, strings.Repeat("~", len(lineContent)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(writer, resetEscapeSequence)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FlattenDiagnosticMessage(d *ast.Diagnostic, newLine string) string {
|
|
||||||
var output strings.Builder
|
|
||||||
WriteFlattenedDiagnosticMessage(&output, d, newLine)
|
|
||||||
return output.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteFlattenedDiagnosticMessage(writer io.Writer, diagnostic *ast.Diagnostic, newline string) {
|
|
||||||
fmt.Fprint(writer, diagnostic.Message())
|
|
||||||
|
|
||||||
for _, chain := range diagnostic.MessageChain() {
|
|
||||||
flattenDiagnosticMessageChain(writer, chain, newline, 1 /*level*/)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func flattenDiagnosticMessageChain(writer io.Writer, chain *ast.Diagnostic, newLine string, level int) {
|
|
||||||
fmt.Fprint(writer, newLine)
|
|
||||||
for range level {
|
|
||||||
fmt.Fprint(writer, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(writer, chain.Message())
|
|
||||||
for _, child := range chain.MessageChain() {
|
|
||||||
flattenDiagnosticMessageChain(writer, child, newLine, level+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCategoryFormat(category diagnostics.Category) string {
|
|
||||||
switch category {
|
|
||||||
case diagnostics.CategoryError:
|
|
||||||
return foregroundColorEscapeRed
|
|
||||||
case diagnostics.CategoryWarning:
|
|
||||||
return foregroundColorEscapeYellow
|
|
||||||
case diagnostics.CategorySuggestion:
|
|
||||||
return foregroundColorEscapeGrey
|
|
||||||
case diagnostics.CategoryMessage:
|
|
||||||
return foregroundColorEscapeBlue
|
|
||||||
}
|
|
||||||
panic("Unhandled diagnostic category")
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormattedWriter func(output io.Writer, text string, formatStyle string)
|
|
||||||
|
|
||||||
func writeWithStyleAndReset(output io.Writer, text string, formatStyle string) {
|
|
||||||
fmt.Fprint(output, formatStyle)
|
|
||||||
fmt.Fprint(output, text)
|
|
||||||
fmt.Fprint(output, resetEscapeSequence)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteLocation(output io.Writer, file *ast.SourceFile, pos int, formatOpts *FormattingOptions, writeWithStyleAndReset FormattedWriter) {
|
|
||||||
firstLine, firstChar := scanner.GetECMALineAndCharacterOfPosition(file, pos)
|
|
||||||
var relativeFileName string
|
|
||||||
if formatOpts != nil {
|
|
||||||
relativeFileName = tspath.ConvertToRelativePath(file.FileName(), formatOpts.ComparePathsOptions)
|
|
||||||
} else {
|
|
||||||
relativeFileName = file.FileName()
|
|
||||||
}
|
|
||||||
|
|
||||||
writeWithStyleAndReset(output, relativeFileName, foregroundColorEscapeCyan)
|
|
||||||
fmt.Fprint(output, ":")
|
|
||||||
writeWithStyleAndReset(output, strconv.Itoa(firstLine+1), foregroundColorEscapeYellow)
|
|
||||||
fmt.Fprint(output, ":")
|
|
||||||
writeWithStyleAndReset(output, strconv.Itoa(firstChar+1), foregroundColorEscapeYellow)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some of these lived in watch.ts, but they're not specific to the watch API.
|
|
||||||
|
|
||||||
type ErrorSummary struct {
|
|
||||||
TotalErrorCount int
|
|
||||||
GlobalErrors []*ast.Diagnostic
|
|
||||||
ErrorsByFiles map[*ast.SourceFile][]*ast.Diagnostic
|
|
||||||
SortedFileList []*ast.SourceFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteErrorSummaryText(output io.Writer, allDiagnostics []*ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
// Roughly corresponds to 'getErrorSummaryText' from watch.ts
|
|
||||||
|
|
||||||
errorSummary := getErrorSummary(allDiagnostics)
|
|
||||||
totalErrorCount := errorSummary.TotalErrorCount
|
|
||||||
if totalErrorCount == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
firstFile := &ast.SourceFile{}
|
|
||||||
if len(errorSummary.SortedFileList) > 0 {
|
|
||||||
firstFile = errorSummary.SortedFileList[0]
|
|
||||||
}
|
|
||||||
firstFileName := prettyPathForFileError(firstFile, errorSummary.ErrorsByFiles[firstFile], formatOpts)
|
|
||||||
numErroringFiles := len(errorSummary.ErrorsByFiles)
|
|
||||||
|
|
||||||
var message string
|
|
||||||
if totalErrorCount == 1 {
|
|
||||||
// Special-case a single error.
|
|
||||||
if len(errorSummary.GlobalErrors) > 0 || firstFileName == "" {
|
|
||||||
message = diagnostics.Found_1_error.Format()
|
|
||||||
} else {
|
|
||||||
message = diagnostics.Found_1_error_in_0.Format(firstFileName)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch numErroringFiles {
|
|
||||||
case 0:
|
|
||||||
// No file-specific errors.
|
|
||||||
message = diagnostics.Found_0_errors.Format(totalErrorCount)
|
|
||||||
case 1:
|
|
||||||
// One file with errors.
|
|
||||||
message = diagnostics.Found_0_errors_in_the_same_file_starting_at_Colon_1.Format(totalErrorCount, firstFileName)
|
|
||||||
default:
|
|
||||||
// Multiple files with errors.
|
|
||||||
message = diagnostics.Found_0_errors_in_1_files.Format(totalErrorCount, numErroringFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
fmt.Fprint(output, message)
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
if numErroringFiles > 1 {
|
|
||||||
writeTabularErrorsDisplay(output, errorSummary, formatOpts)
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getErrorSummary(diags []*ast.Diagnostic) *ErrorSummary {
|
|
||||||
var totalErrorCount int
|
|
||||||
var globalErrors []*ast.Diagnostic
|
|
||||||
var errorsByFiles map[*ast.SourceFile][]*ast.Diagnostic
|
|
||||||
|
|
||||||
for _, diagnostic := range diags {
|
|
||||||
if diagnostic.Category() != diagnostics.CategoryError {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
totalErrorCount++
|
|
||||||
if diagnostic.File() == nil {
|
|
||||||
globalErrors = append(globalErrors, diagnostic)
|
|
||||||
} else {
|
|
||||||
if errorsByFiles == nil {
|
|
||||||
errorsByFiles = make(map[*ast.SourceFile][]*ast.Diagnostic)
|
|
||||||
}
|
|
||||||
errorsByFiles[diagnostic.File()] = append(errorsByFiles[diagnostic.File()], diagnostic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// !!!
|
|
||||||
// Need an ordered map here, but sorting for consistency.
|
|
||||||
sortedFileList := slices.SortedFunc(maps.Keys(errorsByFiles), func(a, b *ast.SourceFile) int {
|
|
||||||
return strings.Compare(a.FileName(), b.FileName())
|
|
||||||
})
|
|
||||||
|
|
||||||
return &ErrorSummary{
|
|
||||||
TotalErrorCount: totalErrorCount,
|
|
||||||
GlobalErrors: globalErrors,
|
|
||||||
ErrorsByFiles: errorsByFiles,
|
|
||||||
SortedFileList: sortedFileList,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeTabularErrorsDisplay(output io.Writer, errorSummary *ErrorSummary, formatOpts *FormattingOptions) {
|
|
||||||
sortedFiles := errorSummary.SortedFileList
|
|
||||||
|
|
||||||
maxErrors := 0
|
|
||||||
for _, errorsForFile := range errorSummary.ErrorsByFiles {
|
|
||||||
maxErrors = max(maxErrors, len(errorsForFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
// !!!
|
|
||||||
// TODO (drosen): This was never localized.
|
|
||||||
// Should make this better.
|
|
||||||
headerRow := diagnostics.Errors_Files.Message()
|
|
||||||
leftColumnHeadingLength := len(strings.Split(headerRow, " ")[0])
|
|
||||||
lengthOfBiggestErrorCount := len(strconv.Itoa(maxErrors))
|
|
||||||
leftPaddingGoal := max(leftColumnHeadingLength, lengthOfBiggestErrorCount)
|
|
||||||
headerPadding := max(lengthOfBiggestErrorCount-leftColumnHeadingLength, 0)
|
|
||||||
|
|
||||||
fmt.Fprint(output, strings.Repeat(" ", headerPadding))
|
|
||||||
fmt.Fprint(output, headerRow)
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
|
|
||||||
for _, file := range sortedFiles {
|
|
||||||
fileErrors := errorSummary.ErrorsByFiles[file]
|
|
||||||
errorCount := len(fileErrors)
|
|
||||||
|
|
||||||
fmt.Fprintf(output, "%*d ", leftPaddingGoal, errorCount)
|
|
||||||
fmt.Fprint(output, prettyPathForFileError(file, fileErrors, formatOpts))
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func prettyPathForFileError(file *ast.SourceFile, fileErrors []*ast.Diagnostic, formatOpts *FormattingOptions) string {
|
|
||||||
if file == nil || len(fileErrors) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
line, _ := scanner.GetECMALineAndCharacterOfPosition(file, fileErrors[0].Loc().Pos())
|
|
||||||
fileName := file.FileName()
|
|
||||||
if tspath.PathIsAbsolute(fileName) && tspath.PathIsAbsolute(formatOpts.CurrentDirectory) {
|
|
||||||
fileName = tspath.ConvertToRelativePath(file.FileName(), formatOpts.ComparePathsOptions)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s%s:%d%s",
|
|
||||||
fileName,
|
|
||||||
foregroundColorEscapeGrey,
|
|
||||||
line+1,
|
|
||||||
resetEscapeSequence,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteFormatDiagnostics(output io.Writer, diagnostics []*ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
for _, diagnostic := range diagnostics {
|
|
||||||
WriteFormatDiagnostic(output, diagnostic, formatOpts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteFormatDiagnostic(output io.Writer, diagnostic *ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
if diagnostic.File() != nil {
|
|
||||||
line, character := scanner.GetECMALineAndCharacterOfPosition(diagnostic.File(), diagnostic.Loc().Pos())
|
|
||||||
fileName := diagnostic.File().FileName()
|
|
||||||
relativeFileName := tspath.ConvertToRelativePath(fileName, formatOpts.ComparePathsOptions)
|
|
||||||
fmt.Fprintf(output, "%s(%d,%d): ", relativeFileName, line+1, character+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(output, "%s TS%d: ", diagnostic.Category().Name(), diagnostic.Code())
|
|
||||||
WriteFlattenedDiagnosticMessage(output, diagnostic, formatOpts.NewLine)
|
|
||||||
fmt.Fprint(output, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatDiagnosticsStatusWithColorAndTime(output io.Writer, time string, diag *ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
fmt.Fprint(output, "[")
|
|
||||||
writeWithStyleAndReset(output, time, foregroundColorEscapeGrey)
|
|
||||||
fmt.Fprint(output, "] ")
|
|
||||||
WriteFlattenedDiagnosticMessage(output, diag, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatDiagnosticsStatusAndTime(output io.Writer, time string, diag *ast.Diagnostic, formatOpts *FormattingOptions) {
|
|
||||||
fmt.Fprint(output, time, " - ")
|
|
||||||
WriteFlattenedDiagnosticMessage(output, diag, formatOpts.NewLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ScreenStartingCodes = []int32{
|
|
||||||
diagnostics.Starting_compilation_in_watch_mode.Code(),
|
|
||||||
diagnostics.File_change_detected_Starting_incremental_compilation.Code(),
|
|
||||||
}
|
|
||||||
|
|
||||||
func TryClearScreen(output io.Writer, diag *ast.Diagnostic, options *core.CompilerOptions) bool {
|
|
||||||
if !options.PreserveWatchOutput.IsTrue() &&
|
|
||||||
!options.ExtendedDiagnostics.IsTrue() &&
|
|
||||||
!options.Diagnostics.IsTrue() &&
|
|
||||||
slices.Contains(ScreenStartingCodes, diag.Code()) {
|
|
||||||
fmt.Fprint(output, "\x1B[2J\x1B[3J\x1B[H") // Clear screen and move cursor to home position
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user