Skip to content

Commit 10ea5bf

Browse files
committed
Script infos while opening/closing shouldnt mark project as dirty if the contents dont change
1 parent 17565d8 commit 10ea5bf

6 files changed

Lines changed: 169 additions & 135 deletions

File tree

src/harness/unittests/cachingInServerLSHost.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace ts {
6161
typingsInstaller: undefined
6262
};
6363
const projectService = new server.ProjectService(svcOpts);
64-
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, serverHost);
64+
const rootScriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(server.toNormalizedPath(rootFile), /*openedByClient*/ true);
6565

6666
const project = projectService.assignOrphanScriptInfoToInferredProject(rootScriptInfo);
6767
project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } );

src/harness/unittests/textStorage.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace ts.textStorage {
2121
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
2222
const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path));
2323

24-
ts1.useScriptVersionCache();
24+
ts1.useScriptVersionCache_TestOnly();
2525
ts2.useText();
2626

2727
const lineMap = computeLineStarts(f.content);
@@ -55,16 +55,16 @@ namespace ts.textStorage {
5555
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
5656

5757
ts1.getSnapshot();
58-
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 1");
58+
assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 1");
5959

6060
ts1.edit(0, 5, " ");
61-
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 1");
61+
assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 1");
6262

6363
ts1.useText();
64-
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 2");
64+
assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 2");
6565

6666
ts1.getLineInfo(0);
67-
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 2");
67+
assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 2");
6868
});
6969
});
70-
}
70+
}

src/server/editorServices.ts

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ namespace ts.server {
687687
else {
688688
// file has been changed which might affect the set of referenced files in projects that include
689689
// this file and set of inferred projects
690-
info.reloadFromFile();
690+
info.delayReloadNonMixedContentFile();
691691
this.delayUpdateProjectGraphs(info.containingProjects);
692692
}
693693
}
@@ -753,7 +753,7 @@ namespace ts.server {
753753
const configFileSpecs = project.configFileSpecs;
754754
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilationSettings(), project.getCachedPartialSystem(), this.hostConfiguration.extraFileExtensions);
755755
project.updateErrorOnNoInputFiles(result.fileNames.length !== 0);
756-
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader, /*clientFileName*/ undefined);
756+
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader);
757757
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
758758
}
759759
},
@@ -1352,7 +1352,7 @@ namespace ts.server {
13521352
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader),
13531353
options.compileOnSave === undefined ? true : options.compileOnSave);
13541354

1355-
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typeAcquisition);
1355+
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, typeAcquisition);
13561356
this.externalProjects.push(project);
13571357
this.sendProjectTelemetry(project.externalProjectName, project);
13581358
return project;
@@ -1401,14 +1401,14 @@ namespace ts.server {
14011401
}
14021402
}
14031403

1404-
private addFilesToNonInferredProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition): void {
1405-
this.updateNonInferredProjectFiles(project, files, propertyReader, clientFileName);
1404+
private addFilesToNonInferredProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, typeAcquisition: TypeAcquisition): void {
1405+
this.updateNonInferredProjectFiles(project, files, propertyReader);
14061406
project.setTypeAcquisition(typeAcquisition);
14071407
// This doesnt need scheduling since its either creation or reload of the project
14081408
project.updateGraph();
14091409
}
14101410

1411-
private createConfiguredProject(configFileName: NormalizedPath, clientFileName?: string) {
1411+
private createConfiguredProject(configFileName: NormalizedPath) {
14121412
const cachedPartialSystem = createCachedPartialSystem(this.host);
14131413
const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedPartialSystem);
14141414
this.logger.info(`Opened configuration file ${configFileName}`);
@@ -1438,14 +1438,14 @@ namespace ts.server {
14381438
}
14391439

14401440
project.setProjectErrors(configFileErrors);
1441-
this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition);
1441+
this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, projectOptions.typeAcquisition);
14421442
this.configuredProjects.set(project.canonicalConfigFilePath, project);
14431443
this.setConfigFileExistenceByNewConfiguredProject(project);
14441444
this.sendProjectTelemetry(project.getConfigFilePath(), project, projectOptions);
14451445
return project;
14461446
}
14471447

1448-
private updateNonInferredProjectFiles<T>(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName?: string) {
1448+
private updateNonInferredProjectFiles<T>(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader<T>) {
14491449
const projectRootFilesMap = project.getRootFilesMap();
14501450
const newRootScriptInfoMap = createMap<ProjectRoot>();
14511451

@@ -1467,7 +1467,7 @@ namespace ts.server {
14671467
else {
14681468
const scriptKind = propertyReader.getScriptKind(f);
14691469
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
1470-
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.partialSystem);
1470+
scriptInfo = this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(normalizedPath, scriptKind, hasMixedContent, project.partialSystem);
14711471
path = scriptInfo.path;
14721472
// If this script info is not already a root add it
14731473
if (!project.isRoot(scriptInfo)) {
@@ -1509,7 +1509,7 @@ namespace ts.server {
15091509
if (compileOnSave !== undefined) {
15101510
project.compileOnSaveEnabled = compileOnSave;
15111511
}
1512-
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, /*clientFileName*/ undefined, newTypeAcquisition);
1512+
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, newTypeAcquisition);
15131513
}
15141514

15151515
/**
@@ -1617,15 +1617,11 @@ namespace ts.server {
16171617
return project;
16181618
}
16191619

1620-
/**
1621-
* @param uncheckedFileName is absolute pathname
1622-
* @param fileContent is a known version of the file content that is more up to date than the one on disk
1623-
*/
16241620
/*@internal*/
1625-
getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, hostToQueryFileExistsOn: PartialSystem) {
1626-
return this.getOrCreateScriptInfoForNormalizedPath(
1627-
toNormalizedPath(uncheckedFileName), openedByClient, /*fileContent*/ undefined,
1628-
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, hostToQueryFileExistsOn
1621+
getOrCreateScriptInfoNotOpenedByClient(uncheckedFileName: string, hostToQueryFileExistsOn: PartialSystem) {
1622+
return this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(
1623+
toNormalizedPath(uncheckedFileName), /*scriptKind*/ undefined,
1624+
/*hasMixedContent*/ undefined, hostToQueryFileExistsOn
16291625
);
16301626
}
16311627

@@ -1654,38 +1650,41 @@ namespace ts.server {
16541650
}
16551651
}
16561652

1653+
getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) {
1654+
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
1655+
}
1656+
1657+
getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) {
1658+
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
1659+
}
1660+
16571661
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) {
1662+
Debug.assert(fileContent === undefined || openedByClient, "ScriptInfo needs to be opened by client to be able to set its user defined content");
16581663
const path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName);
16591664
let info = this.getScriptInfoForPath(path);
16601665
if (!info) {
1661-
if (openedByClient || (hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
1662-
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path);
1663-
1664-
this.filenameToScriptInfo.set(info.path, info);
1665-
1666-
if (openedByClient) {
1667-
if (fileContent === undefined) {
1668-
// if file is opened by client and its content is not specified - use file text
1669-
fileContent = this.host.readFile(fileName) || "";
1670-
}
1671-
}
1672-
else {
1673-
this.watchClosedScriptInfo(info);
1674-
}
1666+
// If the file is not opened by client and the file doesnot exist on the disk, return
1667+
if (!openedByClient && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
1668+
return;
16751669
}
1676-
}
1677-
if (info) {
1678-
if (openedByClient && !info.isScriptOpen()) {
1679-
this.stopWatchingScriptInfo(info);
1680-
info.open(fileContent);
1681-
if (hasMixedContent) {
1682-
info.registerFileUpdate();
1683-
}
1670+
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path);
1671+
this.filenameToScriptInfo.set(info.path, info);
1672+
if (!openedByClient) {
1673+
this.watchClosedScriptInfo(info);
16841674
}
1685-
else if (fileContent !== undefined) {
1686-
info.reload(fileContent);
1675+
}
1676+
if (openedByClient && !info.isScriptOpen()) {
1677+
// Opening closed script info
1678+
// either it was created just now, or was part of projects but was closed
1679+
this.stopWatchingScriptInfo(info);
1680+
info.open(fileContent);
1681+
if (hasMixedContent) {
1682+
info.registerFileUpdate();
16871683
}
16881684
}
1685+
else {
1686+
Debug.assert(fileContent === undefined);
1687+
}
16891688
return info;
16901689
}
16911690

@@ -1730,9 +1729,16 @@ namespace ts.server {
17301729

17311730
/**
17321731
* This function rebuilds the project for every file opened by the client
1732+
* This does not reload contents of open files from disk. But we could do that if needed
17331733
*/
17341734
reloadProjects() {
17351735
this.logger.info("reload projects.");
1736+
// If we want this to also reload open files from disk, we could do that,
1737+
// but then we need to make sure we arent calling this function
1738+
// (and would separate out below reloading of projects to be called when immediate reload is needed)
1739+
// as there is no need to load contents of the files from the disk
1740+
1741+
// Reload Projects
17361742
this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false);
17371743
this.refreshInferredProjects();
17381744
}
@@ -1771,7 +1777,7 @@ namespace ts.server {
17711777
if (configFileName) {
17721778
const project = this.findConfiguredProjectByProjectName(configFileName);
17731779
if (!project) {
1774-
this.createConfiguredProject(configFileName, info.fileName);
1780+
this.createConfiguredProject(configFileName);
17751781
updatedProjects.set(configFileName, true);
17761782
}
17771783
else if (!updatedProjects.has(configFileName)) {
@@ -1862,14 +1868,14 @@ namespace ts.server {
18621868
let configFileName: NormalizedPath;
18631869
let configFileErrors: ReadonlyArray<Diagnostic>;
18641870

1865-
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
1871+
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, fileContent, scriptKind, hasMixedContent);
18661872
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
18671873
if (!project) {
18681874
configFileName = this.getConfigFileNameForFile(info, projectRootPath);
18691875
if (configFileName) {
18701876
project = this.findConfiguredProjectByProjectName(configFileName);
18711877
if (!project) {
1872-
project = this.createConfiguredProject(configFileName, fileName);
1878+
project = this.createConfiguredProject(configFileName);
18731879

18741880
// even if opening config file was successful, it could still
18751881
// contain errors that were tolerated.

src/server/project.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ namespace ts.server {
263263
}
264264

265265
private getScriptInfoLSHost(fileName: string) {
266-
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.partialSystem);
266+
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.partialSystem);
267267
if (scriptInfo) {
268268
const existingValue = this.rootFilesMap.get(scriptInfo.path);
269269
if (existingValue !== undefined && existingValue !== scriptInfo) {
@@ -804,7 +804,7 @@ namespace ts.server {
804804
// by the LSHost for files in the program when the program is retrieved above but
805805
// the program doesn't contain external files so this must be done explicitly.
806806
inserted => {
807-
const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.partialSystem);
807+
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.partialSystem);
808808
scriptInfo.attachToProject(this);
809809
},
810810
removed => {
@@ -845,9 +845,8 @@ namespace ts.server {
845845
}
846846

847847
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
848-
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(
849-
fileName, /*openedByClient*/ false, /*fileContent*/ undefined,
850-
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem
848+
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(
849+
fileName, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem
851850
);
852851
if (scriptInfo && !scriptInfo.isAttached(this)) {
853852
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);

0 commit comments

Comments
 (0)