mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
introduce ThrottledOperations
This commit is contained in:
parent
cad8049482
commit
641c2ffd5e
@ -108,7 +108,6 @@ namespace ts.server {
|
||||
currentPath = parentPath;
|
||||
parentPath = getDirectoryPath(parentPath);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,21 +138,21 @@ namespace ts.server {
|
||||
/**
|
||||
* list of open files
|
||||
*/
|
||||
openFiles: ScriptInfo[] = [];
|
||||
readonly openFiles: ScriptInfo[] = [];
|
||||
|
||||
private readonly directoryWatchers: DirectoryWatchers;
|
||||
private readonly throttledOperations: ThrottledOperations;
|
||||
|
||||
private readonly hostConfiguration: HostConfiguration;
|
||||
|
||||
private timerForDetectingProjectFileListChanges: Map<any> = {};
|
||||
|
||||
constructor(public readonly host: ServerHost,
|
||||
public readonly logger: Logger,
|
||||
public readonly cancellationToken: HostCancellationToken,
|
||||
private readonly useOneInferredProject: boolean,
|
||||
private readonly useSingleInferredProject: boolean,
|
||||
private readonly eventHandler?: ProjectServiceEventHandler) {
|
||||
|
||||
this.directoryWatchers = new DirectoryWatchers(this);
|
||||
this.throttledOperations = new ThrottledOperations(host);
|
||||
// ts.disableIncrementalParsing = true;
|
||||
|
||||
this.hostConfiguration = {
|
||||
@ -243,14 +242,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
this.log(`Detected source file changes: ${fileName}`);
|
||||
const timeoutId = this.timerForDetectingProjectFileListChanges[project.configFileName];
|
||||
if (timeoutId) {
|
||||
this.host.clearTimeout(timeoutId);
|
||||
}
|
||||
this.timerForDetectingProjectFileListChanges[project.configFileName] = this.host.setTimeout(
|
||||
() => this.handleChangeInSourceFileForConfiguredProject(project),
|
||||
250
|
||||
);
|
||||
this.throttledOperations.schedule(
|
||||
project.configFileName,
|
||||
250,
|
||||
() => this.handleChangeInSourceFileForConfiguredProject(project));
|
||||
}
|
||||
|
||||
private handleChangeInSourceFileForConfiguredProject(project: ConfiguredProject) {
|
||||
@ -357,7 +352,7 @@ namespace ts.server {
|
||||
// create new inferred project p with the newly opened file as root
|
||||
// or add root to existing inferred project if 'useOneInferredProject' is true
|
||||
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info);
|
||||
if (!this.useOneInferredProject) {
|
||||
if (!this.useSingleInferredProject) {
|
||||
|
||||
// if useOneInferredProject is not set then try to fixup ownership of open files
|
||||
for (const f of this.openFiles) {
|
||||
@ -755,7 +750,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) {
|
||||
const useExistingProject = this.useOneInferredProject && this.inferredProjects.length;
|
||||
const useExistingProject = this.useSingleInferredProject && this.inferredProjects.length;
|
||||
const project = useExistingProject
|
||||
? this.inferredProjects[0]
|
||||
: new InferredProject(this, this.documentRegistry, /*languageServiceEnabled*/ true);
|
||||
@ -1004,42 +999,42 @@ namespace ts.server {
|
||||
// this.openFilesReferenced.push(rootFile);
|
||||
// }
|
||||
// }
|
||||
// if (rootFile.containingProjects.some(p => p.projectKind !== ProjectKind.Inferred)) {
|
||||
// // file was included in non-inferred project - drop old inferred project
|
||||
// if (rootFile.containingProjects.some(p => p.projectKind !== ProjectKind.Inferred)) {
|
||||
// // file was included in non-inferred project - drop old inferred project
|
||||
|
||||
// }
|
||||
// else {
|
||||
// openFileRoots.push(rootFile);
|
||||
// }
|
||||
// let inferredProjectsToRemove: Project[];
|
||||
// for (const p of rootFile.containingProjects) {
|
||||
// if (p.projectKind !== ProjectKind.Inferred) {
|
||||
// // file was included in non-inferred project - drop old inferred project
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// openFileRoots.push(rootFile);
|
||||
// }
|
||||
// let inferredProjectsToRemove: Project[];
|
||||
// for (const p of rootFile.containingProjects) {
|
||||
// if (p.projectKind !== ProjectKind.Inferred) {
|
||||
// // file was included in non-inferred project - drop old inferred project
|
||||
// }
|
||||
// }
|
||||
|
||||
// const rootedProject = rootFile.defaultProject;
|
||||
// const referencingProjects = this.findReferencingProjects(rootFile, rootedProject);
|
||||
// const rootedProject = rootFile.defaultProject;
|
||||
// const referencingProjects = this.findReferencingProjects(rootFile, rootedProject);
|
||||
|
||||
// if (rootFile.defaultProject && rootFile.defaultProject.projectKind !== ProjectKind.Inferred) {
|
||||
// // If the root file has already been added into a configured project,
|
||||
// // meaning the original inferred project is gone already.
|
||||
// if (rootedProject.projectKind === ProjectKind.Inferred) {
|
||||
// this.removeProject(rootedProject);
|
||||
// }
|
||||
// this.openFileRootsConfigured.push(rootFile);
|
||||
// }
|
||||
// else {
|
||||
// if (referencingProjects.length === 0) {
|
||||
// rootFile.defaultProject = rootedProject;
|
||||
// openFileRoots.push(rootFile);
|
||||
// }
|
||||
// else {
|
||||
// // remove project from inferred projects list because root captured
|
||||
// this.removeProject(rootedProject);
|
||||
// this.openFilesReferenced.push(rootFile);
|
||||
// }
|
||||
// }
|
||||
// if (rootFile.defaultProject && rootFile.defaultProject.projectKind !== ProjectKind.Inferred) {
|
||||
// // If the root file has already been added into a configured project,
|
||||
// // meaning the original inferred project is gone already.
|
||||
// if (rootedProject.projectKind === ProjectKind.Inferred) {
|
||||
// this.removeProject(rootedProject);
|
||||
// }
|
||||
// this.openFileRootsConfigured.push(rootFile);
|
||||
// }
|
||||
// else {
|
||||
// if (referencingProjects.length === 0) {
|
||||
// rootFile.defaultProject = rootedProject;
|
||||
// openFileRoots.push(rootFile);
|
||||
// }
|
||||
// else {
|
||||
// // remove project from inferred projects list because root captured
|
||||
// this.removeProject(rootedProject);
|
||||
// this.openFilesReferenced.push(rootFile);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// this.openFileRoots = openFileRoots;
|
||||
|
||||
|
||||
@ -91,8 +91,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
class IOSession extends Session {
|
||||
constructor(host: ServerHost, cancellationToken: HostCancellationToken, useOneInferredProject: boolean, logger: ts.server.Logger) {
|
||||
super(host, cancellationToken, useOneInferredProject, Buffer.byteLength, process.hrtime, logger);
|
||||
constructor(host: ServerHost, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, logger: ts.server.Logger) {
|
||||
super(host, cancellationToken, useSingleInferredProject, Buffer.byteLength, process.hrtime, logger);
|
||||
}
|
||||
|
||||
exit() {
|
||||
@ -304,8 +304,8 @@ namespace ts.server {
|
||||
};
|
||||
};
|
||||
|
||||
const useOneInferredProject = sys.args.some(arg => arg === "--useOneInferredProject");
|
||||
const ioSession = new IOSession(sys, cancellationToken, useOneInferredProject, logger);
|
||||
const useSingleInferredProject = sys.args.some(arg => arg === "--useSingleInferredProject");
|
||||
const ioSession = new IOSession(sys, cancellationToken, useSingleInferredProject, logger);
|
||||
process.on("uncaughtException", function(err: Error) {
|
||||
ioSession.logError(err, "unknown");
|
||||
});
|
||||
|
||||
@ -179,12 +179,12 @@ namespace ts.server {
|
||||
constructor(
|
||||
private host: ServerHost,
|
||||
cancellationToken: HostCancellationToken,
|
||||
useOneInferredProject: boolean,
|
||||
useSingleInferredProject: boolean,
|
||||
private byteLength: (buf: string, encoding?: string) => number,
|
||||
private hrtime: (start?: number[]) => number[],
|
||||
private logger: Logger) {
|
||||
this.projectService =
|
||||
new ProjectService(host, logger, cancellationToken, useOneInferredProject, (eventName, project, fileName) => {
|
||||
new ProjectService(host, logger, cancellationToken, useSingleInferredProject, (eventName, project, fileName) => {
|
||||
this.handleEvent(eventName, project, fileName);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
/// <reference path="..\services\services.ts" />
|
||||
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
namespace ts.server {
|
||||
export interface Logger {
|
||||
close(): void;
|
||||
@ -74,14 +72,16 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export interface NormalizedPathMap<T> {
|
||||
get (path: NormalizedPath): T;
|
||||
set (path: NormalizedPath, value: T): void;
|
||||
get(path: NormalizedPath): T;
|
||||
set(path: NormalizedPath, value: T): void;
|
||||
contains(path: NormalizedPath): boolean;
|
||||
remove(path: NormalizedPath): void;
|
||||
}
|
||||
|
||||
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
const map: Map<T> = Object.create(null);
|
||||
/* tslint:enable:no-null-keyword */
|
||||
return {
|
||||
get(path) {
|
||||
return map[path];
|
||||
@ -97,48 +97,49 @@ namespace ts.server {
|
||||
}
|
||||
};
|
||||
}
|
||||
function throwLanguageServiceIsDisabledError() {;
|
||||
function throwLanguageServiceIsDisabledError() {
|
||||
;
|
||||
throw new Error("LanguageService is disabled");
|
||||
}
|
||||
|
||||
export const nullLanguageService: LanguageService = {
|
||||
cleanupSemanticCache: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSyntacticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSemanticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getCompilerOptionsDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
cleanupSemanticCache: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSyntacticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSemanticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getCompilerOptionsDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getEncodedSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getEncodedSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getCompletionsAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
findReferences: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getCompletionEntryDetails: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getQuickInfoAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
findRenameLocations: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNameOrDottedNameSpan: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getBreakpointStatementAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getBraceMatchingAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSignatureHelpItems: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getRenameInfo: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getTypeDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getReferencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getDocumentHighlights: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getOccurrencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNavigateToItems: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNavigationBarItems: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getOutliningSpans: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getTodoComments: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getIndentationAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getFormattingEditsForRange: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getFormattingEditsForDocument: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getFormattingEditsAfterKeystroke: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getDocCommentTemplateAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
isValidBraceCompletionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getEmitOutput: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getProgram: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNonBoundSourceFile: (): any => throwLanguageServiceIsDisabledError(),
|
||||
dispose: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getEncodedSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getCompletionsAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
findReferences: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getCompletionEntryDetails: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getQuickInfoAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
findRenameLocations: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNameOrDottedNameSpan: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getBreakpointStatementAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getBraceMatchingAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getSignatureHelpItems: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getRenameInfo: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getTypeDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getReferencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getDocumentHighlights: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getOccurrencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNavigateToItems: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNavigationBarItems: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getOutliningSpans: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getTodoComments: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getIndentationAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getFormattingEditsForRange: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getFormattingEditsForDocument: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getFormattingEditsAfterKeystroke: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getDocCommentTemplateAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
isValidBraceCompletionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getEmitOutput: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getProgram: (): any => throwLanguageServiceIsDisabledError(),
|
||||
getNonBoundSourceFile: (): any => throwLanguageServiceIsDisabledError(),
|
||||
dispose: (): any => throwLanguageServiceIsDisabledError(),
|
||||
};
|
||||
|
||||
export interface ServerLanguageServiceHost {
|
||||
@ -172,4 +173,24 @@ namespace ts.server {
|
||||
export function makeInferredProjectName(counter: number) {
|
||||
return `/dev/null/inferredProject${counter}*`;
|
||||
}
|
||||
|
||||
export class ThrottledOperations {
|
||||
private pendingTimeouts: Map<any> = {};
|
||||
constructor(private readonly host: ServerHost) {
|
||||
}
|
||||
|
||||
public schedule(operationId: string, delay: number, cb: () => void) {
|
||||
if (hasProperty(this.pendingTimeouts, operationId)) {
|
||||
// another operation was already scheduled for this id - cancel it
|
||||
this.host.clearTimeout(this.pendingTimeouts[operationId]);
|
||||
}
|
||||
// schedule new operation, pass arguments
|
||||
this.pendingTimeouts[operationId] = this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb);
|
||||
}
|
||||
|
||||
private static run(self: ThrottledOperations, operationId: string, cb: () => void) {
|
||||
delete self.pendingTimeouts[operationId];
|
||||
cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,7 +145,10 @@ namespace ts {
|
||||
private fs: ts.FileMap<FSEntry>;
|
||||
private getCanonicalFileName: (s: string) => string;
|
||||
private toPath: (f: string) => Path;
|
||||
private callbackQueue: TimeOutCallback[] = [];
|
||||
|
||||
private nextTimeoutId = 0;
|
||||
private callbacks: { [n: number]: TimeOutCallback } = {};
|
||||
|
||||
readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {};
|
||||
readonly watchedFiles: Map<FileWatcherCallback[]> = {};
|
||||
|
||||
@ -283,25 +286,28 @@ namespace ts {
|
||||
}
|
||||
|
||||
// TOOD: record and invoke callbacks to simulate timer events
|
||||
readonly setTimeout = (callback: TimeOutCallback, time: number) => {
|
||||
this.callbackQueue.push(callback);
|
||||
return this.callbackQueue.length - 1;
|
||||
readonly setTimeout = (callback: TimeOutCallback, time: number, ...args: any[]) => {
|
||||
const timeoutId = this.nextTimeoutId;
|
||||
this.nextTimeoutId++;
|
||||
this.callbacks[timeoutId] = callback.bind(undefined, ...args);
|
||||
return timeoutId;
|
||||
};
|
||||
readonly clearTimeout = (timeoutId: any): void => {
|
||||
if (typeof timeoutId === "number") {
|
||||
this.callbackQueue.splice(timeoutId, 1);
|
||||
delete this.callbacks[timeoutId];
|
||||
}
|
||||
};
|
||||
|
||||
checkTimeoutQueueLength(expected: number) {
|
||||
assert.equal(this.callbackQueue.length, expected, `expected ${expected} timeout callbacks queued but found ${this.callbackQueue.length}.`);
|
||||
const callbacksCount = sizeOfMap(this.callbacks);
|
||||
assert.equal(callbacksCount, expected, `expected ${expected} timeout callbacks queued but found ${callbacksCount}.`);
|
||||
}
|
||||
|
||||
runQueuedTimeoutCallbacks() {
|
||||
for (const callback of this.callbackQueue) {
|
||||
callback();
|
||||
for (const id in this.callbacks) {
|
||||
this.callbacks[id]();
|
||||
}
|
||||
this.callbackQueue = [];
|
||||
this.callbacks = [];
|
||||
}
|
||||
|
||||
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user