181 lines
5.4 KiB
Go
181 lines
5.4 KiB
Go
package project
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/collections"
|
|
"github.com/microsoft/typescript-go/internal/compiler"
|
|
"github.com/microsoft/typescript-go/internal/ls/autoimport"
|
|
"github.com/microsoft/typescript-go/internal/packagejson"
|
|
"github.com/microsoft/typescript-go/internal/tspath"
|
|
"github.com/microsoft/typescript-go/internal/vfs"
|
|
)
|
|
|
|
type autoImportBuilderFS struct {
|
|
snapshotFSBuilder *snapshotFSBuilder
|
|
untrackedFiles collections.SyncMap[tspath.Path, FileHandle]
|
|
}
|
|
|
|
var _ FileSource = (*autoImportBuilderFS)(nil)
|
|
|
|
// FS implements FileSource.
|
|
func (a *autoImportBuilderFS) FS() vfs.FS {
|
|
return a.snapshotFSBuilder.fs
|
|
}
|
|
|
|
// GetFile implements FileSource.
|
|
func (a *autoImportBuilderFS) GetFile(fileName string) FileHandle {
|
|
path := a.snapshotFSBuilder.toPath(fileName)
|
|
return a.GetFileByPath(fileName, path)
|
|
}
|
|
|
|
// GetFileByPath implements FileSource.
|
|
func (a *autoImportBuilderFS) GetFileByPath(fileName string, path tspath.Path) FileHandle {
|
|
// We want to avoid long-term caching of files referenced only by auto-imports, so we
|
|
// override GetFileByPath to avoid collecting more files into the snapshotFSBuilder's
|
|
// diskFiles. (Note the reason we can't just use the finalized SnapshotFS is that changed
|
|
// files not read during other parts of the snapshot clone will be marked as dirty, but
|
|
// not yet refreshed from disk.)
|
|
if overlay, ok := a.snapshotFSBuilder.overlays[path]; ok {
|
|
return overlay
|
|
}
|
|
if diskFile, ok := a.snapshotFSBuilder.diskFiles.Load(path); ok {
|
|
return a.snapshotFSBuilder.reloadEntryIfNeeded(diskFile)
|
|
}
|
|
if fh, ok := a.untrackedFiles.Load(path); ok {
|
|
return fh
|
|
}
|
|
var fh FileHandle
|
|
content, ok := a.snapshotFSBuilder.fs.ReadFile(fileName)
|
|
if ok {
|
|
fh = newDiskFile(fileName, content)
|
|
}
|
|
fh, _ = a.untrackedFiles.LoadOrStore(path, fh)
|
|
return fh
|
|
}
|
|
|
|
func (a *autoImportBuilderFS) GetAccessibleEntries(path string) vfs.Entries {
|
|
return a.snapshotFSBuilder.GetAccessibleEntries(path)
|
|
}
|
|
|
|
// FileExists implements FileSource.
|
|
func (a *autoImportBuilderFS) FileExists(fileName string, path tspath.Path) bool {
|
|
return a.snapshotFSBuilder.FileExists(fileName, path)
|
|
}
|
|
|
|
type autoImportRegistryCloneHost struct {
|
|
projectCollection *ProjectCollection
|
|
parseCache *ParseCache
|
|
fs *sourceFS
|
|
currentDirectory string
|
|
|
|
filesMu sync.Mutex
|
|
files []ParseCacheKey
|
|
}
|
|
|
|
var _ autoimport.RegistryCloneHost = (*autoImportRegistryCloneHost)(nil)
|
|
|
|
func newAutoImportRegistryCloneHost(
|
|
projectCollection *ProjectCollection,
|
|
parseCache *ParseCache,
|
|
snapshotFSBuilder *snapshotFSBuilder,
|
|
currentDirectory string,
|
|
toPath func(fileName string) tspath.Path,
|
|
) *autoImportRegistryCloneHost {
|
|
return &autoImportRegistryCloneHost{
|
|
projectCollection: projectCollection,
|
|
parseCache: parseCache,
|
|
fs: newSourceFS(false, &autoImportBuilderFS{snapshotFSBuilder: snapshotFSBuilder}, toPath),
|
|
currentDirectory: currentDirectory,
|
|
}
|
|
}
|
|
|
|
// FS implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) FS() vfs.FS {
|
|
return a.fs
|
|
}
|
|
|
|
// GetCurrentDirectory implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) GetCurrentDirectory() string {
|
|
return a.currentDirectory
|
|
}
|
|
|
|
// GetDefaultProject implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) GetDefaultProject(path tspath.Path) (tspath.Path, *compiler.Program) {
|
|
project := a.projectCollection.GetDefaultProject(path)
|
|
if project == nil {
|
|
return "", nil
|
|
}
|
|
return project.configFilePath, project.GetProgram()
|
|
}
|
|
|
|
// GetPackageJson implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) GetPackageJson(fileName string) *packagejson.InfoCacheEntry {
|
|
// !!! ref-counted shared cache
|
|
fh := a.fs.GetFile(fileName)
|
|
packageDirectory := tspath.GetDirectoryPath(fileName)
|
|
if fh == nil {
|
|
return &packagejson.InfoCacheEntry{
|
|
DirectoryExists: a.fs.DirectoryExists(packageDirectory),
|
|
PackageDirectory: packageDirectory,
|
|
}
|
|
}
|
|
fields, err := packagejson.Parse([]byte(fh.Content()))
|
|
if err != nil {
|
|
return &packagejson.InfoCacheEntry{
|
|
DirectoryExists: true,
|
|
PackageDirectory: tspath.GetDirectoryPath(fileName),
|
|
Contents: &packagejson.PackageJson{
|
|
Parseable: false,
|
|
},
|
|
}
|
|
}
|
|
return &packagejson.InfoCacheEntry{
|
|
DirectoryExists: true,
|
|
PackageDirectory: tspath.GetDirectoryPath(fileName),
|
|
Contents: &packagejson.PackageJson{
|
|
Fields: fields,
|
|
Parseable: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetProgramForProject implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) GetProgramForProject(projectPath tspath.Path) *compiler.Program {
|
|
project := a.projectCollection.GetProjectByPath(projectPath)
|
|
if project == nil {
|
|
return nil
|
|
}
|
|
return project.GetProgram()
|
|
}
|
|
|
|
// GetSourceFile implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) GetSourceFile(fileName string, path tspath.Path) *ast.SourceFile {
|
|
fh := a.fs.GetFile(fileName)
|
|
if fh == nil {
|
|
return nil
|
|
}
|
|
opts := ast.SourceFileParseOptions{
|
|
FileName: fileName,
|
|
Path: path,
|
|
}
|
|
key := NewParseCacheKey(opts, fh.Hash(), fh.Kind())
|
|
result := a.parseCache.Acquire(key, fh)
|
|
|
|
a.filesMu.Lock()
|
|
a.files = append(a.files, key)
|
|
a.filesMu.Unlock()
|
|
|
|
return result
|
|
}
|
|
|
|
// Dispose implements autoimport.RegistryCloneHost.
|
|
func (a *autoImportRegistryCloneHost) Dispose() {
|
|
a.filesMu.Lock()
|
|
defer a.filesMu.Unlock()
|
|
for _, key := range a.files {
|
|
a.parseCache.Deref(key)
|
|
}
|
|
}
|