Skip to content

Commit c1ec7ef

Browse files
authored
remove projects that were no present in the input list in openExternalProjects (#12699)
remove projects that were no present in the input list in openExternalProjects
1 parent 4e9bc69 commit c1ec7ef

File tree

3 files changed

+87
-6
lines changed

3 files changed

+87
-6
lines changed

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,66 @@ namespace ts.projectSystem {
727727
checkNumberOfInferredProjects(projectService, 1);
728728
});
729729

730+
it("remove not-listed external projects", () => {
731+
const f1 = {
732+
path: "/a/app.ts",
733+
content: "let x = 1"
734+
};
735+
const f2 = {
736+
path: "/b/app.ts",
737+
content: "let x = 1"
738+
};
739+
const f3 = {
740+
path: "/c/app.ts",
741+
content: "let x = 1"
742+
};
743+
const makeProject = (f: FileOrFolder) => ({ projectFileName: f.path + ".csproj", rootFiles: [toExternalFile(f.path)], options: {} });
744+
const p1 = makeProject(f1);
745+
const p2 = makeProject(f2);
746+
const p3 = makeProject(f3);
747+
748+
const host = createServerHost([f1, f2, f3]);
749+
const session = createSession(host);
750+
751+
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
752+
seq: 1,
753+
type: "request",
754+
command: "openExternalProjects",
755+
arguments: { projects: [p1, p2] }
756+
});
757+
758+
const projectService = session.getProjectService();
759+
checkNumberOfProjects(projectService, { externalProjects: 2 });
760+
assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName);
761+
assert.equal(projectService.externalProjects[1].getProjectName(), p2.projectFileName);
762+
763+
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
764+
seq: 2,
765+
type: "request",
766+
command: "openExternalProjects",
767+
arguments: { projects: [p1, p3] }
768+
});
769+
checkNumberOfProjects(projectService, { externalProjects: 2 });
770+
assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName);
771+
assert.equal(projectService.externalProjects[1].getProjectName(), p3.projectFileName);
772+
773+
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
774+
seq: 3,
775+
type: "request",
776+
command: "openExternalProjects",
777+
arguments: { projects: [] }
778+
});
779+
checkNumberOfProjects(projectService, { externalProjects: 0 });
780+
781+
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
782+
seq: 3,
783+
type: "request",
784+
command: "openExternalProjects",
785+
arguments: { projects: [p2] }
786+
});
787+
assert.equal(projectService.externalProjects[0].getProjectName(), p2.projectFileName);
788+
});
789+
730790
it("handle recreated files correctly", () => {
731791
const configFile: FileOrFolder = {
732792
path: "/a/b/tsconfig.json",

src/server/editorServices.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,28 @@ namespace ts.server {
13271327
}
13281328
}
13291329

1330-
openExternalProject(proj: protocol.ExternalProject): void {
1330+
openExternalProjects(projects: protocol.ExternalProject[]): void {
1331+
// record project list before the update
1332+
const projectsToClose = arrayToMap(this.externalProjects, p => p.getProjectName(), _ => true);
1333+
for (const externalProjectName in this.externalProjectToConfiguredProjectMap) {
1334+
projectsToClose[externalProjectName] = true;
1335+
}
1336+
1337+
for (const externalProject of projects) {
1338+
this.openExternalProject(externalProject, /*suppressRefreshOfInferredProjects*/ true);
1339+
// delete project that is present in input list
1340+
delete projectsToClose[externalProject.projectFileName];
1341+
}
1342+
1343+
// close projects that were missing in the input list
1344+
for (const externalProjectName in projectsToClose) {
1345+
this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true)
1346+
}
1347+
1348+
this.refreshInferredProjects();
1349+
}
1350+
1351+
openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void {
13311352
// typingOptions has been deprecated and is only supported for backward compatibility
13321353
// purposes. It should be removed in future releases - use typeAcquisition instead.
13331354
if (proj.typingOptions && !proj.typeAcquisition) {
@@ -1420,7 +1441,9 @@ namespace ts.server {
14201441
delete this.externalProjectToConfiguredProjectMap[proj.projectFileName];
14211442
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition);
14221443
}
1423-
this.refreshInferredProjects();
1444+
if (!suppressRefreshOfInferredProjects) {
1445+
this.refreshInferredProjects();
1446+
}
14241447
}
14251448
}
14261449
}

src/server/session.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,14 +1365,12 @@ namespace ts.server {
13651365

13661366
private handlers = createMap<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({
13671367
[CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
1368-
this.projectService.openExternalProject(request.arguments);
1368+
this.projectService.openExternalProject(request.arguments, /*suppressRefreshOfInferredProjects*/ false);
13691369
// TODO: report errors
13701370
return this.requiredResponse(true);
13711371
},
13721372
[CommandNames.OpenExternalProjects]: (request: protocol.OpenExternalProjectsRequest) => {
1373-
for (const proj of request.arguments.projects) {
1374-
this.projectService.openExternalProject(proj);
1375-
}
1373+
this.projectService.openExternalProjects(request.arguments.projects);
13761374
// TODO: report errors
13771375
return this.requiredResponse(true);
13781376
},

0 commit comments

Comments
 (0)