vendor tsgo
This commit is contained in:
202
tools/tsgo/internal/project/configfileregistry.go
Normal file
202
tools/tsgo/internal/project/configfileregistry.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"iter"
|
||||
"maps"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/tsoptions"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
type ConfigFileRegistry struct {
|
||||
// configs is a map of config file paths to their entries.
|
||||
configs map[tspath.Path]*configFileEntry
|
||||
// configFileNames is a map of open file paths to information
|
||||
// about their ancestor config file names. It is only used as
|
||||
// a cache during
|
||||
configFileNames map[tspath.Path]*configFileNames
|
||||
// customConfigFileName is the custom config file name preference that was
|
||||
// used when building this registry's configFileNames cache.
|
||||
customConfigFileName string
|
||||
}
|
||||
|
||||
type configFileEntry struct {
|
||||
fileName string
|
||||
pendingReload PendingReload
|
||||
commandLine *tsoptions.ParsedCommandLine
|
||||
// retainingProjects is the set of projects that have called acquireConfig
|
||||
// without releasing it. A config file entry may be acquired by a project
|
||||
// either because it is the config for that project or because it is the
|
||||
// config for a referenced project.
|
||||
retainingProjects map[tspath.Path]struct{}
|
||||
// retainingOpenFiles is the set of open files that caused this config to
|
||||
// load during project collection building. This config file may or may not
|
||||
// end up being the config for the default project for these files, but
|
||||
// determining the default project loaded this config as a candidate, so
|
||||
// subsequent calls to `projectCollectionBuilder.findDefaultConfiguredProject`
|
||||
// will use this config as part of the search, so it must be retained.
|
||||
retainingOpenFiles map[tspath.Path]struct{}
|
||||
// retainingConfigs is the set of config files that extend this one. This
|
||||
// provides a cheap reverse mapping for a project config's
|
||||
// `commandLine.ExtendedSourceFiles()` that can be used to notify the
|
||||
// extending projects when this config changes. An extended config file may
|
||||
// or may not also be used directly by a project, so it's possible that
|
||||
// when this is set, no other fields will be used.
|
||||
retainingConfigs map[tspath.Path]struct{}
|
||||
// rootFilesWatch is a watch for the root files of this config file.
|
||||
rootFilesWatch *WatchedFiles[PatternsAndIgnored]
|
||||
}
|
||||
|
||||
func newConfigFileEntry(hasRelativePatternCapability bool, fileName string) *configFileEntry {
|
||||
return &configFileEntry{
|
||||
fileName: fileName,
|
||||
pendingReload: PendingReloadFull,
|
||||
rootFilesWatch: NewWatchedFiles(
|
||||
"root files for "+fileName,
|
||||
lsproto.WatchKindCreate|lsproto.WatchKindChange|lsproto.WatchKindDelete,
|
||||
hasRelativePatternCapability,
|
||||
core.Identity,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func newExtendedConfigFileEntry(fileName string, extendingConfigPath tspath.Path) *configFileEntry {
|
||||
return &configFileEntry{
|
||||
fileName: fileName,
|
||||
pendingReload: PendingReloadFull,
|
||||
retainingConfigs: map[tspath.Path]struct{}{extendingConfigPath: {}},
|
||||
}
|
||||
}
|
||||
|
||||
func (e *configFileEntry) Clone() *configFileEntry {
|
||||
return &configFileEntry{
|
||||
fileName: e.fileName,
|
||||
pendingReload: e.pendingReload,
|
||||
commandLine: e.commandLine,
|
||||
// !!! eagerly cloning these maps makes everything more convenient,
|
||||
// but it could be avoided if needed.
|
||||
retainingProjects: maps.Clone(e.retainingProjects),
|
||||
retainingOpenFiles: maps.Clone(e.retainingOpenFiles),
|
||||
retainingConfigs: maps.Clone(e.retainingConfigs),
|
||||
rootFilesWatch: e.rootFilesWatch,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigFileRegistry) GetConfig(path tspath.Path) *tsoptions.ParsedCommandLine {
|
||||
if entry, ok := c.configs[path]; ok {
|
||||
return entry.commandLine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConfigFileRegistry) GetConfigFileName(path tspath.Path) string {
|
||||
if entry, ok := c.configFileNames[path]; ok {
|
||||
return entry.nearestConfigFileName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *ConfigFileRegistry) GetAncestorConfigFileName(path tspath.Path, higherThanConfig string) string {
|
||||
if entry, ok := c.configFileNames[path]; ok {
|
||||
return entry.ancestors[higherThanConfig]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// clone creates a shallow copy of the configFileRegistry.
|
||||
func (c *ConfigFileRegistry) clone() *ConfigFileRegistry {
|
||||
return &ConfigFileRegistry{
|
||||
configs: maps.Clone(c.configs),
|
||||
configFileNames: maps.Clone(c.configFileNames),
|
||||
customConfigFileName: c.customConfigFileName,
|
||||
}
|
||||
}
|
||||
|
||||
// For testing
|
||||
type TestConfigEntry struct {
|
||||
FileName string
|
||||
RetainingProjects iter.Seq[tspath.Path]
|
||||
RetainingOpenFiles iter.Seq[tspath.Path]
|
||||
RetainingConfigs iter.Seq[tspath.Path]
|
||||
}
|
||||
|
||||
// For testing
|
||||
func (c *ConfigFileRegistry) ForEachTestConfigEntry(cb func(tspath.Path, *TestConfigEntry)) {
|
||||
if c != nil {
|
||||
for path, entry := range c.configs {
|
||||
cb(path, &TestConfigEntry{
|
||||
FileName: entry.fileName,
|
||||
RetainingProjects: maps.Keys(entry.retainingProjects),
|
||||
RetainingOpenFiles: maps.Keys(entry.retainingOpenFiles),
|
||||
RetainingConfigs: maps.Keys(entry.retainingConfigs),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For testing
|
||||
func (c *ConfigFileRegistry) GetTestConfigEntry(path tspath.Path) *TestConfigEntry {
|
||||
if c != nil {
|
||||
if entry, ok := c.configs[path]; ok {
|
||||
return &TestConfigEntry{
|
||||
FileName: entry.fileName,
|
||||
RetainingProjects: maps.Keys(entry.retainingProjects),
|
||||
RetainingOpenFiles: maps.Keys(entry.retainingOpenFiles),
|
||||
RetainingConfigs: maps.Keys(entry.retainingConfigs),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestConfigFileNamesEntry struct {
|
||||
NearestConfigFileName string
|
||||
Ancestors map[string]string
|
||||
}
|
||||
|
||||
// For testing
|
||||
func (c *ConfigFileRegistry) ForEachTestConfigFileNamesEntry(cb func(tspath.Path, *TestConfigFileNamesEntry)) {
|
||||
if c != nil {
|
||||
for path, entry := range c.configFileNames {
|
||||
cb(path, &TestConfigFileNamesEntry{
|
||||
NearestConfigFileName: entry.nearestConfigFileName,
|
||||
Ancestors: entry.ancestors,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For testing
|
||||
func (c *ConfigFileRegistry) GetTestConfigFileNamesEntry(path tspath.Path) *TestConfigFileNamesEntry {
|
||||
if c != nil {
|
||||
if entry, ok := c.configFileNames[path]; ok {
|
||||
return &TestConfigFileNamesEntry{
|
||||
NearestConfigFileName: entry.nearestConfigFileName,
|
||||
Ancestors: entry.ancestors,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type configFileNames struct {
|
||||
// nearestConfigFileName is the file name of the nearest ancestor config file.
|
||||
nearestConfigFileName string
|
||||
// ancestors is a map from one ancestor config file path to the next.
|
||||
// For example, if `/a`, `/a/b`, and `/a/b/c` all contain config files,
|
||||
// the fully loaded map will look like:
|
||||
// {
|
||||
// "/a/b/c/tsconfig.json": "/a/b/tsconfig.json",
|
||||
// "/a/b/tsconfig.json": "/a/tsconfig.json"
|
||||
// }
|
||||
ancestors map[string]string
|
||||
}
|
||||
|
||||
func (c *configFileNames) Clone() *configFileNames {
|
||||
return &configFileNames{
|
||||
nearestConfigFileName: c.nearestConfigFileName,
|
||||
ancestors: maps.Clone(c.ancestors),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user