Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace ts.server {

// tslint:disable variable-name
export const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
export const LargeFileReferencedEvent = "largeFileReferenced";
export const ConfigFileDiagEvent = "configFileDiag";
export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
export const ProjectInfoTelemetryEvent = "projectInfo";
Expand All @@ -16,6 +17,11 @@ namespace ts.server {
data: { openFiles: string[]; };
}

export interface LargeFileReferencedEvent {
eventName: typeof LargeFileReferencedEvent;
data: { file: string; fileSize: number; maxFileSize: number; };
}

export interface ConfigFileDiagEvent {
eventName: typeof ConfigFileDiagEvent;
data: { triggerFile: string, configFileName: string, diagnostics: ReadonlyArray<Diagnostic> };
Expand Down Expand Up @@ -92,7 +98,7 @@ namespace ts.server {
readonly checkJs: boolean;
}

export type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
export type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;

export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;

Expand Down Expand Up @@ -645,6 +651,19 @@ namespace ts.server {
this.eventHandler(event);
}

/* @internal */
sendLargeFileReferencedEvent(file: string, fileSize: number) {
if (!this.eventHandler) {
return;
}

const event: LargeFileReferencedEvent = {
eventName: LargeFileReferencedEvent,
data: { file, fileSize, maxFileSize }
};
this.eventHandler(event);
}

/* @internal */
delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project: Project) {
this.delayUpdateProjectGraph(project);
Expand Down
21 changes: 21 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,27 @@ namespace ts.server.protocol {
openFiles: string[];
}

export type LargeFileReferencedEventName = "largeFileReferenced";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this code duplicated? I guess that’s one public and the other private, but why aren’t they shared?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because we want to be able to build protocol.ts separately as a standalone to protocol.d.ts which editors consume.

export interface LargeFileReferencedEvent extends Event {
event: LargeFileReferencedEventName;
body: LargeFileReferencedEventBody;
}

export interface LargeFileReferencedEventBody {
/**
* name of the large file being loaded
*/
file: string;
/**
* size of the file
*/
fileSize: number;
/**
* max file size allowed on the server
*/
maxFileSize: number;
}

/**
* Arguments for reload request.
*/
Expand Down
18 changes: 13 additions & 5 deletions src/server/scriptInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace ts.server {
*/
private pendingReloadFromDisk: boolean;

constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath, initialVersion?: ScriptInfoVersion) {
constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath, initialVersion: ScriptInfoVersion | undefined, private readonly info: ScriptInfo) {
this.version = initialVersion || { svc: 0, text: 0 };
}

Expand Down Expand Up @@ -164,9 +164,17 @@ namespace ts.server {

private getFileText(tempFileName?: string) {
let text: string;
const getText = () => text === undefined ? (text = this.host.readFile(tempFileName || this.fileName) || "") : text;
const size = this.host.getFileSize ? this.host.getFileSize(tempFileName || this.fileName) : getText().length;
return size > maxFileSize ? "" : getText();
const fileName = tempFileName || this.fileName;
const getText = () => text === undefined ? (text = this.host.readFile(fileName) || "") : text;
const fileSize = this.host.getFileSize ? this.host.getFileSize(fileName) : getText().length;
if (fileSize > maxFileSize) {
Debug.assert(!!this.info.containingProjects.length);
const service = this.info.containingProjects[0].projectService;
service.logger.info(`Skipped loading contents of large file ${fileName} for info ${this.info.fileName}: fileSize: ${fileSize}`);
this.info.containingProjects[0].projectService.sendLargeFileReferencedEvent(fileName, fileSize);
return "";
}
return getText();
}

private switchToScriptVersionCache(): ScriptVersionCache {
Expand Down Expand Up @@ -248,7 +256,7 @@ namespace ts.server {
initialVersion?: ScriptInfoVersion) {
this.isDynamic = isDynamicFileName(fileName);

this.textStorage = new TextStorage(host, fileName, initialVersion);
this.textStorage = new TextStorage(host, fileName, initialVersion, this);
if (hasMixedContent || this.isDynamic) {
this.textStorage.reload("");
this.realpath = this.path;
Expand Down
4 changes: 4 additions & 0 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,10 @@ namespace ts.server {
const { openFiles } = event.data;
this.projectsUpdatedInBackgroundEvent(openFiles);
break;
case LargeFileReferencedEvent:
const { file, fileSize, maxFileSize } = event.data;
this.event<protocol.LargeFileReferencedEventBody>({ file, fileSize, maxFileSize }, "largeFileReferenced");
break;
case ConfigFileDiagEvent:
const { triggerFile, configFileName: configFile, diagnostics } = event.data;
const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
Expand Down
9 changes: 5 additions & 4 deletions src/testRunner/unittests/textStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ namespace ts.textStorage {
it("text based storage should be have exactly the same as script version cache", () => {

const host = projectSystem.createServerHost([f]);

const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path));
// Since script info is not used in these tests, just cheat by passing undefined
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path), /*initialVersion*/ undefined, /*info*/undefined!);
const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path), /*initialVersion*/ undefined, /*info*/undefined!);

ts1.useScriptVersionCache_TestOnly();
ts2.useText();
Expand Down Expand Up @@ -48,7 +48,8 @@ namespace ts.textStorage {

it("should switch to script version cache if necessary", () => {
const host = projectSystem.createServerHost([f]);
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
// Since script info is not used in these tests, just cheat by passing undefined
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path), /*initialVersion*/ undefined, /*info*/undefined!);

ts1.getSnapshot();
assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 1");
Expand Down
39 changes: 32 additions & 7 deletions src/testRunner/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2843,7 +2843,7 @@ namespace ts.projectSystem {
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent) {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent || e.eventName === server.LargeFileReferencedEvent) {
return;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
Expand Down Expand Up @@ -9028,6 +9028,27 @@ export const x = 10;`
fileSize: server.maxFileSize + 1
};

function createSessionWithEventHandler(host: TestServerHost) {
const largeFileReferencedEvents: server.LargeFileReferencedEvent[] = [];
const session = createSession(host, {
eventHandler: e => {
if (e.eventName === server.LargeFileReferencedEvent) {
largeFileReferencedEvents.push(e);
}
}
});

return { session, verifyLargeFileReferencedEvent };

function verifyLargeFileReferencedEvent() {
assert.equal(largeFileReferencedEvents.length, 1);
assert.deepEqual(largeFileReferencedEvents, [{
eventName: server.LargeFileReferencedEvent,
data: { file: largeFile.path, fileSize: largeFile.fileSize, maxFileSize: server.maxFileSize }
}]);
}
}

it("when large file is included by tsconfig", () => {
const file: File = {
path: `${projectRoot}/src/file.ts`,
Expand All @@ -9039,13 +9060,15 @@ export const x = 10;`
};
const files = [file, largeFile, libFile, tsconfig];
const host = createServerHost(files);
const service = createProjectService(host);
service.openClientFile(file.path);
service.checkNumberOfProjects({ configuredProjects: 1 });
const { session, verifyLargeFileReferencedEvent } = createSessionWithEventHandler(host);
const service = session.getProjectService();
openFilesForSession([file], session);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(tsconfig.path)!;
checkProjectActualFiles(project, [file.path, libFile.path, largeFile.path, tsconfig.path]);
const info = service.getScriptInfo(largeFile.path)!;
assert.equal(info.cacheSourceFile.sourceFile.text, "");
verifyLargeFileReferencedEvent();
});

it("when large file is included by module resolution", () => {
Expand All @@ -9055,13 +9078,15 @@ export const x = 10;`
};
const files = [file, largeFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host);
service.openClientFile(file.path);
service.checkNumberOfProjects({ inferredProjects: 1 });
const { session, verifyLargeFileReferencedEvent } = createSessionWithEventHandler(host);
const service = session.getProjectService();
openFilesForSession([file], session);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [file.path, libFile.path, largeFile.path]);
const info = service.getScriptInfo(largeFile.path)!;
assert.equal(info.cacheSourceFile.sourceFile.text, "");
verifyLargeFileReferencedEvent();
});
});

Expand Down
30 changes: 29 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7657,6 +7657,25 @@ declare namespace ts.server.protocol {
*/
openFiles: string[];
}
type LargeFileReferencedEventName = "largeFileReferenced";
interface LargeFileReferencedEvent extends Event {
event: LargeFileReferencedEventName;
body: LargeFileReferencedEventBody;
}
interface LargeFileReferencedEventBody {
/**
* name of the large file being loaded
*/
file: string;
/**
* size of the file
*/
fileSize: number;
/**
* max file size allowed on the server
*/
maxFileSize: number;
}
/**
* Arguments for reload request.
*/
Expand Down Expand Up @@ -8377,6 +8396,7 @@ declare namespace ts.server {
declare namespace ts.server {
const maxProgramSizeForNonTsFiles: number;
const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
const LargeFileReferencedEvent = "largeFileReferenced";
const ConfigFileDiagEvent = "configFileDiag";
const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
const ProjectInfoTelemetryEvent = "projectInfo";
Expand All @@ -8387,6 +8407,14 @@ declare namespace ts.server {
openFiles: string[];
};
}
interface LargeFileReferencedEvent {
eventName: typeof LargeFileReferencedEvent;
data: {
file: string;
fileSize: number;
maxFileSize: number;
};
}
interface ConfigFileDiagEvent {
eventName: typeof ConfigFileDiagEvent;
data: {
Expand Down Expand Up @@ -8457,7 +8485,7 @@ declare namespace ts.server {
interface OpenFileInfo {
readonly checkJs: boolean;
}
type ProjectServiceEvent = ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
interface SafeList {
[name: string]: {
Expand Down