diff --git a/tsc/internal/api/session_apistate_test.go b/tsc/internal/api/session_apistate_test.go index 585e2aa3442fc..3ec3a729fd1d2 100644 --- a/tsc/internal/api/session_apistate_test.go +++ b/tsc/internal/api/session_apistate_test.go @@ -544,3 +544,30 @@ func TestOpenProjectsWithCircularExtendsReportCircularity(t *testing.T) { } assert.DeepEqual(t, codes, []int32{18000}) } + +func TestGetCurrentLanguageServerSnapshotCloseProjectRetainsProjectForOpenFile(t *testing.T) { + t.Parallel() + + const configFileName = "/home/projects/p/tsconfig.json" + const fileName = "/home/projects/p/index.ts" + projectSession, _ := projecttestutil.Setup(map[string]any{ + configFileName: `{}`, + fileName: `export const value = 1;`, + }) + defer projectSession.Close() + ctx := context.Background() + session := NewLSPSession(projectSession, nil) + defer session.Close() + cfg := DocumentIdentifier{FileName: configFileName} + _, err := session.handleGetCurrentLanguageServerSnapshot(ctx, &GetCurrentLanguageServerSnapshotParams{Changes: &LanguageServerSnapshotChanges{ + OpenProjects: []DocumentIdentifier{cfg}, + OpenFiles: []DocumentIdentifier{{FileName: fileName}}, + }}) + assert.NilError(t, err) + + _, err = session.handleGetCurrentLanguageServerSnapshot(ctx, &GetCurrentLanguageServerSnapshotParams{Changes: &LanguageServerSnapshotChanges{ + CloseProjects: []DocumentIdentifier{cfg}, + }}) + assert.NilError(t, err) + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.PathKeyFromCanonical(configFileName)) != nil) +} diff --git a/tsc/internal/project/projectcollectionbuilder.go b/tsc/internal/project/projectcollectionbuilder.go index a5236abcb11ec..b4aeccb157215 100644 --- a/tsc/internal/project/projectcollectionbuilder.go +++ b/tsc/internal/project/projectcollectionbuilder.go @@ -256,6 +256,13 @@ func (b *ProjectCollectionBuilder) HandleAPIRequest(apiRequest *APISnapshotReque } } + // Projects that are still the default for API-opened files must be retained. + for path, file := range b.apiState.openFiles { + if entry := b.findDefaultConfiguredProject(file.fileName, path); entry != nil { + delete(projectsToClose, entry.Value().configFilePath) + } + } + for projectPath := range projectsToClose { if entry, ok := b.configuredProjects.Load(ConfiguredProjectIDFromPathKey(projectPath)); ok { b.deleteProject(entry, logger)