mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-02 14:48:32 -05:00
Script infos while opening/closing shouldnt mark project as dirty if the contents dont change
This commit is contained in:
@@ -61,7 +61,7 @@ namespace ts {
|
||||
typingsInstaller: undefined
|
||||
};
|
||||
const projectService = new server.ProjectService(svcOpts);
|
||||
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, serverHost);
|
||||
const rootScriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(server.toNormalizedPath(rootFile), /*openedByClient*/ true);
|
||||
|
||||
const project = projectService.assignOrphanScriptInfoToInferredProject(rootScriptInfo);
|
||||
project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } );
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ts.textStorage {
|
||||
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
|
||||
ts1.useScriptVersionCache();
|
||||
ts1.useScriptVersionCache_TestOnly();
|
||||
ts2.useText();
|
||||
|
||||
const lineMap = computeLineStarts(f.content);
|
||||
@@ -55,16 +55,16 @@ namespace ts.textStorage {
|
||||
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
|
||||
ts1.getSnapshot();
|
||||
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 1");
|
||||
assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 1");
|
||||
|
||||
ts1.edit(0, 5, " ");
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 1");
|
||||
assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 1");
|
||||
|
||||
ts1.useText();
|
||||
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 2");
|
||||
assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 2");
|
||||
|
||||
ts1.getLineInfo(0);
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 2");
|
||||
assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 2");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,7 +687,7 @@ namespace ts.server {
|
||||
else {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.reloadFromFile();
|
||||
info.delayReloadNonMixedContentFile();
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
}
|
||||
}
|
||||
@@ -753,7 +753,7 @@ namespace ts.server {
|
||||
const configFileSpecs = project.configFileSpecs;
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilationSettings(), project.getCachedPartialSystem(), this.hostConfiguration.extraFileExtensions);
|
||||
project.updateErrorOnNoInputFiles(result.fileNames.length !== 0);
|
||||
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader, /*clientFileName*/ undefined);
|
||||
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader);
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
}
|
||||
},
|
||||
@@ -1352,7 +1352,7 @@ namespace ts.server {
|
||||
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader),
|
||||
options.compileOnSave === undefined ? true : options.compileOnSave);
|
||||
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typeAcquisition);
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, typeAcquisition);
|
||||
this.externalProjects.push(project);
|
||||
this.sendProjectTelemetry(project.externalProjectName, project);
|
||||
return project;
|
||||
@@ -1401,14 +1401,14 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private addFilesToNonInferredProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition): void {
|
||||
this.updateNonInferredProjectFiles(project, files, propertyReader, clientFileName);
|
||||
private addFilesToNonInferredProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, typeAcquisition: TypeAcquisition): void {
|
||||
this.updateNonInferredProjectFiles(project, files, propertyReader);
|
||||
project.setTypeAcquisition(typeAcquisition);
|
||||
// This doesnt need scheduling since its either creation or reload of the project
|
||||
project.updateGraph();
|
||||
}
|
||||
|
||||
private createConfiguredProject(configFileName: NormalizedPath, clientFileName?: string) {
|
||||
private createConfiguredProject(configFileName: NormalizedPath) {
|
||||
const cachedPartialSystem = createCachedPartialSystem(this.host);
|
||||
const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedPartialSystem);
|
||||
this.logger.info(`Opened configuration file ${configFileName}`);
|
||||
@@ -1438,14 +1438,14 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
project.setProjectErrors(configFileErrors);
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition);
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, projectOptions.typeAcquisition);
|
||||
this.configuredProjects.set(project.canonicalConfigFilePath, project);
|
||||
this.setConfigFileExistenceByNewConfiguredProject(project);
|
||||
this.sendProjectTelemetry(project.getConfigFilePath(), project, projectOptions);
|
||||
return project;
|
||||
}
|
||||
|
||||
private updateNonInferredProjectFiles<T>(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName?: string) {
|
||||
private updateNonInferredProjectFiles<T>(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader<T>) {
|
||||
const projectRootFilesMap = project.getRootFilesMap();
|
||||
const newRootScriptInfoMap = createMap<ProjectRoot>();
|
||||
|
||||
@@ -1467,7 +1467,7 @@ namespace ts.server {
|
||||
else {
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.partialSystem);
|
||||
scriptInfo = this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(normalizedPath, scriptKind, hasMixedContent, project.partialSystem);
|
||||
path = scriptInfo.path;
|
||||
// If this script info is not already a root add it
|
||||
if (!project.isRoot(scriptInfo)) {
|
||||
@@ -1509,7 +1509,7 @@ namespace ts.server {
|
||||
if (compileOnSave !== undefined) {
|
||||
project.compileOnSaveEnabled = compileOnSave;
|
||||
}
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, /*clientFileName*/ undefined, newTypeAcquisition);
|
||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, newTypeAcquisition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1617,15 +1617,11 @@ namespace ts.server {
|
||||
return project;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uncheckedFileName is absolute pathname
|
||||
* @param fileContent is a known version of the file content that is more up to date than the one on disk
|
||||
*/
|
||||
/*@internal*/
|
||||
getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, hostToQueryFileExistsOn: PartialSystem) {
|
||||
return this.getOrCreateScriptInfoForNormalizedPath(
|
||||
toNormalizedPath(uncheckedFileName), openedByClient, /*fileContent*/ undefined,
|
||||
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, hostToQueryFileExistsOn
|
||||
getOrCreateScriptInfoNotOpenedByClient(uncheckedFileName: string, hostToQueryFileExistsOn: PartialSystem) {
|
||||
return this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(
|
||||
toNormalizedPath(uncheckedFileName), /*scriptKind*/ undefined,
|
||||
/*hasMixedContent*/ undefined, hostToQueryFileExistsOn
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1654,38 +1650,41 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) {
|
||||
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
|
||||
}
|
||||
|
||||
getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) {
|
||||
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
|
||||
}
|
||||
|
||||
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) {
|
||||
Debug.assert(fileContent === undefined || openedByClient, "ScriptInfo needs to be opened by client to be able to set its user defined content");
|
||||
const path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName);
|
||||
let info = this.getScriptInfoForPath(path);
|
||||
if (!info) {
|
||||
if (openedByClient || (hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
|
||||
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path);
|
||||
|
||||
this.filenameToScriptInfo.set(info.path, info);
|
||||
|
||||
if (openedByClient) {
|
||||
if (fileContent === undefined) {
|
||||
// if file is opened by client and its content is not specified - use file text
|
||||
fileContent = this.host.readFile(fileName) || "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.watchClosedScriptInfo(info);
|
||||
}
|
||||
// If the file is not opened by client and the file doesnot exist on the disk, return
|
||||
if (!openedByClient && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
|
||||
return;
|
||||
}
|
||||
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path);
|
||||
this.filenameToScriptInfo.set(info.path, info);
|
||||
if (!openedByClient) {
|
||||
this.watchClosedScriptInfo(info);
|
||||
}
|
||||
}
|
||||
if (info) {
|
||||
if (openedByClient && !info.isScriptOpen()) {
|
||||
this.stopWatchingScriptInfo(info);
|
||||
info.open(fileContent);
|
||||
if (hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
}
|
||||
}
|
||||
else if (fileContent !== undefined) {
|
||||
info.reload(fileContent);
|
||||
if (openedByClient && !info.isScriptOpen()) {
|
||||
// Opening closed script info
|
||||
// either it was created just now, or was part of projects but was closed
|
||||
this.stopWatchingScriptInfo(info);
|
||||
info.open(fileContent);
|
||||
if (hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(fileContent === undefined);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -1730,9 +1729,16 @@ namespace ts.server {
|
||||
|
||||
/**
|
||||
* This function rebuilds the project for every file opened by the client
|
||||
* This does not reload contents of open files from disk. But we could do that if needed
|
||||
*/
|
||||
reloadProjects() {
|
||||
this.logger.info("reload projects.");
|
||||
// If we want this to also reload open files from disk, we could do that,
|
||||
// but then we need to make sure we arent calling this function
|
||||
// (and would separate out below reloading of projects to be called when immediate reload is needed)
|
||||
// as there is no need to load contents of the files from the disk
|
||||
|
||||
// Reload Projects
|
||||
this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false);
|
||||
this.refreshInferredProjects();
|
||||
}
|
||||
@@ -1771,7 +1777,7 @@ namespace ts.server {
|
||||
if (configFileName) {
|
||||
const project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
this.createConfiguredProject(configFileName, info.fileName);
|
||||
this.createConfiguredProject(configFileName);
|
||||
updatedProjects.set(configFileName, true);
|
||||
}
|
||||
else if (!updatedProjects.has(configFileName)) {
|
||||
@@ -1862,14 +1868,14 @@ namespace ts.server {
|
||||
let configFileName: NormalizedPath;
|
||||
let configFileErrors: ReadonlyArray<Diagnostic>;
|
||||
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
|
||||
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, fileContent, scriptKind, hasMixedContent);
|
||||
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
|
||||
if (!project) {
|
||||
configFileName = this.getConfigFileNameForFile(info, projectRootPath);
|
||||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
project = this.createConfiguredProject(configFileName, fileName);
|
||||
project = this.createConfiguredProject(configFileName);
|
||||
|
||||
// even if opening config file was successful, it could still
|
||||
// contain errors that were tolerated.
|
||||
|
||||
@@ -263,7 +263,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getScriptInfoLSHost(fileName: string) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.partialSystem);
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.partialSystem);
|
||||
if (scriptInfo) {
|
||||
const existingValue = this.rootFilesMap.get(scriptInfo.path);
|
||||
if (existingValue !== undefined && existingValue !== scriptInfo) {
|
||||
@@ -804,7 +804,7 @@ namespace ts.server {
|
||||
// by the LSHost for files in the program when the program is retrieved above but
|
||||
// the program doesn't contain external files so this must be done explicitly.
|
||||
inserted => {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.partialSystem);
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.partialSystem);
|
||||
scriptInfo.attachToProject(this);
|
||||
},
|
||||
removed => {
|
||||
@@ -845,9 +845,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(
|
||||
fileName, /*openedByClient*/ false, /*fileContent*/ undefined,
|
||||
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(
|
||||
fileName, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem
|
||||
);
|
||||
if (scriptInfo && !scriptInfo.isAttached(this)) {
|
||||
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
|
||||
|
||||
@@ -4,13 +4,19 @@ namespace ts.server {
|
||||
|
||||
/* @internal */
|
||||
export class TextStorage {
|
||||
// Generated only on demand and removes the text if any edits
|
||||
private svc: ScriptVersionCache | undefined;
|
||||
private svcVersion = 0;
|
||||
|
||||
// Store text when there is no svc or svc has no change, on edit, remove the text
|
||||
private text: string;
|
||||
private lineMap: number[];
|
||||
private textVersion = 0;
|
||||
|
||||
public isOpen: boolean;
|
||||
private ownFileText: boolean;
|
||||
private pendingReloadFromDisk: boolean;
|
||||
|
||||
constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath) {
|
||||
}
|
||||
|
||||
@@ -20,43 +26,75 @@ namespace ts.server {
|
||||
: `Text-${this.textVersion}`;
|
||||
}
|
||||
|
||||
public hasScriptVersionCache() {
|
||||
public hasScriptVersionCache_TestOnly() {
|
||||
return this.svc !== undefined;
|
||||
}
|
||||
|
||||
public useScriptVersionCache(newText?: string) {
|
||||
this.switchToScriptVersionCache(newText);
|
||||
public useScriptVersionCache_TestOnly() {
|
||||
this.switchToScriptVersionCache();
|
||||
}
|
||||
|
||||
public useText(newText?: string) {
|
||||
this.svc = undefined;
|
||||
this.setText(newText);
|
||||
this.text = newText;
|
||||
this.lineMap = undefined;
|
||||
this.textVersion++;
|
||||
}
|
||||
|
||||
public edit(start: number, end: number, newText: string) {
|
||||
this.switchToScriptVersionCache().edit(start, end - start, newText);
|
||||
this.ownFileText = false;
|
||||
this.text = undefined;
|
||||
this.lineMap = undefined;
|
||||
}
|
||||
|
||||
public reload(text: string) {
|
||||
if (this.svc) {
|
||||
this.svc.reload(text);
|
||||
}
|
||||
else {
|
||||
this.setText(text);
|
||||
/** returns true if text changed */
|
||||
public reload(newText: string) {
|
||||
Debug.assert(newText !== undefined);
|
||||
|
||||
// Reload always has fresh content
|
||||
this.pendingReloadFromDisk = false;
|
||||
|
||||
// If text changed set the text
|
||||
// This also ensures that if we had switched to version cache,
|
||||
// we are switching back to text.
|
||||
// The change to version cache will happen when needed
|
||||
// Thus avoiding the computation if there are no changes
|
||||
if (this.text !== newText) {
|
||||
this.useText(newText);
|
||||
// We cant guarantee new text is own file text
|
||||
this.ownFileText = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public reloadFromFile(tempFileName?: string) {
|
||||
if (this.svc || (tempFileName !== this.fileName)) {
|
||||
this.reload(this.getFileText(tempFileName));
|
||||
/** returns true if text changed */
|
||||
public reloadFromDisk() {
|
||||
let reloaded = false;
|
||||
if (!this.pendingReloadFromDisk && !this.ownFileText) {
|
||||
reloaded = this.reload(this.getFileText());
|
||||
this.ownFileText = true;
|
||||
}
|
||||
else {
|
||||
this.setText(undefined);
|
||||
return reloaded;
|
||||
}
|
||||
|
||||
public delayReloadFromFileIntoText() {
|
||||
this.pendingReloadFromDisk = true;
|
||||
}
|
||||
|
||||
/** returns true if text changed */
|
||||
public reloadFromFile(tempFileName: string) {
|
||||
let reloaded = false;
|
||||
// Reload if different file or we dont know if we are working with own file text
|
||||
if (tempFileName !== this.fileName || !this.ownFileText) {
|
||||
reloaded = this.reload(this.getFileText(tempFileName));
|
||||
this.ownFileText = !tempFileName || tempFileName === this.fileName;
|
||||
}
|
||||
return reloaded;
|
||||
}
|
||||
|
||||
public getSnapshot(): IScriptSnapshot {
|
||||
return this.svc
|
||||
return this.useScriptVersionCacheIfValidOrOpen()
|
||||
? this.svc.getSnapshot()
|
||||
: ScriptSnapshot.fromString(this.getOrLoadText());
|
||||
}
|
||||
@@ -68,7 +106,7 @@ namespace ts.server {
|
||||
* @param line 0 based index
|
||||
*/
|
||||
lineToTextSpan(line: number): TextSpan {
|
||||
if (!this.svc) {
|
||||
if (!this.useScriptVersionCacheIfValidOrOpen()) {
|
||||
const lineMap = this.getLineMap();
|
||||
const start = lineMap[line]; // -1 since line is 1-based
|
||||
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
|
||||
@@ -82,7 +120,7 @@ namespace ts.server {
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
if (!this.svc) {
|
||||
if (!this.useScriptVersionCacheIfValidOrOpen()) {
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text);
|
||||
}
|
||||
|
||||
@@ -91,7 +129,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
if (!this.svc) {
|
||||
if (!this.useScriptVersionCacheIfValidOrOpen()) {
|
||||
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
|
||||
return { line: line + 1, offset: character + 1 };
|
||||
}
|
||||
@@ -102,43 +140,39 @@ namespace ts.server {
|
||||
return this.host.readFile(tempFileName || this.fileName) || "";
|
||||
}
|
||||
|
||||
private ensureNoScriptVersionCache() {
|
||||
Debug.assert(!this.svc, "ScriptVersionCache should not be set");
|
||||
}
|
||||
|
||||
private switchToScriptVersionCache(newText?: string): ScriptVersionCache {
|
||||
if (!this.svc) {
|
||||
this.svc = ScriptVersionCache.fromString(newText !== undefined ? newText : this.getOrLoadText());
|
||||
private switchToScriptVersionCache(): ScriptVersionCache {
|
||||
if (!this.svc || this.pendingReloadFromDisk) {
|
||||
this.svc = ScriptVersionCache.fromString(this.getOrLoadText());
|
||||
this.svcVersion++;
|
||||
this.text = undefined;
|
||||
}
|
||||
return this.svc;
|
||||
}
|
||||
|
||||
private useScriptVersionCacheIfValidOrOpen(): ScriptVersionCache | undefined {
|
||||
// If this is open script, use the cache
|
||||
if (this.isOpen) {
|
||||
return this.switchToScriptVersionCache();
|
||||
}
|
||||
|
||||
// Else if the svc is uptodate with the text, we are good
|
||||
return !this.pendingReloadFromDisk && this.svc;
|
||||
}
|
||||
|
||||
private getOrLoadText() {
|
||||
this.ensureNoScriptVersionCache();
|
||||
if (this.text === undefined) {
|
||||
this.setText(this.getFileText());
|
||||
if (this.text === undefined || this.pendingReloadFromDisk) {
|
||||
Debug.assert(!this.svc || this.pendingReloadFromDisk, "ScriptVersionCache should not be set when reloading from disk");
|
||||
this.reload(this.getFileText());
|
||||
this.ownFileText = true;
|
||||
}
|
||||
return this.text;
|
||||
}
|
||||
|
||||
private getLineMap() {
|
||||
this.ensureNoScriptVersionCache();
|
||||
Debug.assert(!this.svc, "ScriptVersionCache should not be set");
|
||||
return this.lineMap || (this.lineMap = computeLineStarts(this.getOrLoadText()));
|
||||
}
|
||||
|
||||
private setText(newText: string) {
|
||||
this.ensureNoScriptVersionCache();
|
||||
if (newText === undefined || this.text !== newText) {
|
||||
this.text = newText;
|
||||
this.lineMap = undefined;
|
||||
this.textVersion++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ScriptInfo {
|
||||
/**
|
||||
* All projects that include this file
|
||||
@@ -150,8 +184,6 @@ namespace ts.server {
|
||||
fileWatcher: FileWatcher;
|
||||
private textStorage: TextStorage;
|
||||
|
||||
private isOpen: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly host: ServerHost,
|
||||
readonly fileName: NormalizedPath,
|
||||
@@ -169,19 +201,28 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
public isScriptOpen() {
|
||||
return this.isOpen;
|
||||
return this.textStorage.isOpen;
|
||||
}
|
||||
|
||||
public open(newText: string) {
|
||||
this.isOpen = true;
|
||||
this.textStorage.useScriptVersionCache(newText);
|
||||
this.markContainingProjectsAsDirty();
|
||||
this.textStorage.isOpen = true;
|
||||
if (newText !== undefined &&
|
||||
this.textStorage.reload(newText)) {
|
||||
// reload new contents only if the existing contents changed
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isOpen = false;
|
||||
this.textStorage.useText(this.hasMixedContent ? "" : undefined);
|
||||
this.markContainingProjectsAsDirty();
|
||||
this.textStorage.isOpen = false;
|
||||
if (this.hasMixedContent) {
|
||||
if (this.textStorage.reload("")) {
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
}
|
||||
else if (this.textStorage.reloadFromDisk()) {
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public getSnapshot() {
|
||||
@@ -293,26 +334,31 @@ namespace ts.server {
|
||||
return this.textStorage.getVersion();
|
||||
}
|
||||
|
||||
reload(script: string) {
|
||||
this.textStorage.reload(script);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
saveTo(fileName: string) {
|
||||
const snap = this.textStorage.getSnapshot();
|
||||
this.host.writeFile(fileName, snap.getText(0, snap.getLength()));
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
delayReloadNonMixedContentFile() {
|
||||
Debug.assert(!this.hasMixedContent);
|
||||
this.textStorage.delayReloadFromFileIntoText();
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
reloadFromFile(tempFileName?: NormalizedPath) {
|
||||
if (this.hasMixedContent) {
|
||||
this.reload("");
|
||||
this.textStorage.reload("");
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
else {
|
||||
this.textStorage.reloadFromFile(tempFileName);
|
||||
this.markContainingProjectsAsDirty();
|
||||
if (this.textStorage.reloadFromFile(tempFileName)) {
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.textStorage.getLineInfo(line);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="session.ts" />
|
||||
|
||||
/*@internal*/
|
||||
namespace ts.server {
|
||||
const lineCollectionCapacity = 4;
|
||||
|
||||
@@ -285,24 +286,6 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
// reload whole script, leaving no change history behind reload
|
||||
reload(script: string) {
|
||||
this.currentVersion++;
|
||||
this.changes = []; // history wiped out by reload
|
||||
const snap = new LineIndexSnapshot(this.currentVersion, this, new LineIndex());
|
||||
|
||||
// delete all versions
|
||||
for (let i = 0; i < this.versions.length; i++) {
|
||||
this.versions[i] = undefined;
|
||||
}
|
||||
|
||||
this.versions[this.currentVersionToIndex()] = snap;
|
||||
const lm = LineIndex.linesFromText(script);
|
||||
snap.index.load(lm.lines);
|
||||
|
||||
this.minVersion = this.currentVersion;
|
||||
}
|
||||
|
||||
getSnapshot(): IScriptSnapshot { return this._getSnapshot(); }
|
||||
|
||||
private _getSnapshot(): LineIndexSnapshot {
|
||||
@@ -843,4 +826,4 @@ namespace ts.server {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user