Fix merge

This commit is contained in:
Ryan Cavanaugh 2017-04-13 16:16:57 -07:00
parent d129199f41
commit 3cc379c3c0
9 changed files with 258 additions and 95 deletions

View file

@ -818,13 +818,17 @@ namespace Harness.LanguageService {
// This host is just a proxy for the clientHost, it uses the client // This host is just a proxy for the clientHost, it uses the client
// host to answer server queries about files on disk // host to answer server queries about files on disk
const serverHost = new SessionServerHost(clientHost); const serverHost = new SessionServerHost(clientHost);
const server = new ts.server.Session(serverHost, const opts: ts.server.SessionOptions = {
ts.server.nullCancellationToken, host: serverHost,
/*useOneInferredProject*/ false, cancellationToken: ts.server.nullCancellationToken,
/*typingsInstaller*/ undefined, useSingleInferredProject: false,
Utils.byteLength, typingsInstaller: undefined,
process.hrtime, serverHost, byteLength: Utils.byteLength,
/*canUseEvents*/ true); hrtime: process.hrtime,
logger: serverHost,
canUseEvents: true
};
const server = new ts.server.Session(opts);
// Fake the connection between the client and the server // Fake the connection between the client and the server
serverHost.writeMessage = client.onMessage.bind(client); serverHost.writeMessage = client.onMessage.bind(client);

View file

@ -64,8 +64,16 @@ namespace ts {
getLogFileName: (): string => undefined getLogFileName: (): string => undefined
}; };
const projectService = new server.ProjectService(serverHost, logger, { isCancellationRequested: () => false }, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined); const svcOpts: server.ProjectServiceOptions = {
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /*openedByClient*/ true, /*containingProject*/ undefined); host: serverHost,
logger,
cancellationToken: { isCancellationRequested: () => false },
useSingleInferredProject: false,
typingsInstaller: undefined
};
const projectService = new server.ProjectService(svcOpts);
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */true, /*containingProject*/ undefined);
const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo); const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo);
project.setCompilerOptions({ module: ts.ModuleKind.AMD } ); project.setCompilerOptions({ module: ts.ModuleKind.AMD } );
return { return {

View file

@ -31,6 +31,20 @@ namespace ts.projectSystem {
} }
} }
function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller): server.Session {
const opts: server.SessionOptions = {
host,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller: typingsInstaller || server.nullTypingsInstaller,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: nullLogger,
canUseEvents: false
};
return new server.Session(opts);
}
describe("for configured projects", () => { describe("for configured projects", () => {
let moduleFile1: FileOrFolder; let moduleFile1: FileOrFolder;
let file1Consumer1: FileOrFolder; let file1Consumer1: FileOrFolder;
@ -113,7 +127,7 @@ namespace ts.projectSystem {
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => { it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1, file1Consumer1], session); openFilesForSession([moduleFile1, file1Consumer1], session);
@ -138,7 +152,7 @@ namespace ts.projectSystem {
it("should be up-to-date with the reference map changes", () => { it("should be up-to-date with the reference map changes", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1, file1Consumer1], session); openFilesForSession([moduleFile1, file1Consumer1], session);
@ -185,7 +199,7 @@ namespace ts.projectSystem {
it("should be up-to-date with changes made in non-open files", () => { it("should be up-to-date with changes made in non-open files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1], session); openFilesForSession([moduleFile1], session);
@ -203,7 +217,7 @@ namespace ts.projectSystem {
it("should be up-to-date with deleted files", () => { it("should be up-to-date with deleted files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1], session); openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]); sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
@ -218,7 +232,7 @@ namespace ts.projectSystem {
it("should be up-to-date with newly created files", () => { it("should be up-to-date with newly created files", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1], session); openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]); sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
@ -255,7 +269,7 @@ namespace ts.projectSystem {
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1, file1Consumer1], session); openFilesForSession([moduleFile1, file1Consumer1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]); sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
@ -272,7 +286,7 @@ namespace ts.projectSystem {
it("should return all files if a global file changed shape", () => { it("should return all files if a global file changed shape", () => {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([globalFile3], session); openFilesForSession([globalFile3], session);
const changeGlobalFile3ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, { const changeGlobalFile3ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, {
@ -298,7 +312,7 @@ namespace ts.projectSystem {
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1], session); openFilesForSession([moduleFile1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []); sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []);
}); });
@ -316,7 +330,7 @@ namespace ts.projectSystem {
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1], session); openFilesForSession([moduleFile1], session);
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, { const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, {
@ -345,7 +359,7 @@ namespace ts.projectSystem {
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1], session); openFilesForSession([moduleFile1], session);
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, { const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, {
@ -367,7 +381,7 @@ namespace ts.projectSystem {
}; };
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]); const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([moduleFile1, file1Consumer1], session); openFilesForSession([moduleFile1, file1Consumer1], session);
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]); sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
@ -400,7 +414,7 @@ namespace ts.projectSystem {
}; };
const host = createServerHost([file1, file2, configFile]); const host = createServerHost([file1, file2, configFile]);
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([file1, file2], session); openFilesForSession([file1, file2], session);
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: file1.path }); const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
@ -513,7 +527,7 @@ namespace ts.projectSystem {
}; };
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" }); const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
const typingsInstaller = createTestTypingsInstaller(host); const typingsInstaller = createTestTypingsInstaller(host);
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false); const session = createSession(host, typingsInstaller);
openFilesForSession([file1, file2], session); openFilesForSession([file1, file2], session);
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path }); const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });

View file

@ -50,8 +50,22 @@ namespace ts.server {
let session: TestSession; let session: TestSession;
let lastSent: protocol.Message; let lastSent: protocol.Message;
function createSession(): TestSession {
const opts: server.SessionOptions = {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: mockLogger,
canUseEvents: true
};
return new TestSession(opts);
}
beforeEach(() => { beforeEach(() => {
session = new TestSession(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true); session = createSession();
session.send = (msg: protocol.Message) => { session.send = (msg: protocol.Message) => {
lastSent = msg; lastSent = msg;
}; };
@ -318,7 +332,16 @@ namespace ts.server {
lastSent: protocol.Message; lastSent: protocol.Message;
customHandler = "testhandler"; customHandler = "testhandler";
constructor() { constructor() {
super(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true); super({
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: mockLogger,
canUseEvents: true
});
this.addProtocolHandler(this.customHandler, () => { this.addProtocolHandler(this.customHandler, () => {
return { response: undefined, responseRequired: true }; return { response: undefined, responseRequired: true };
}); });
@ -376,7 +399,16 @@ namespace ts.server {
class InProcSession extends Session { class InProcSession extends Session {
private queue: protocol.Request[] = []; private queue: protocol.Request[] = [];
constructor(private client: InProcClient) { constructor(private client: InProcClient) {
super(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true); super({
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: mockLogger,
canUseEvents: true
});
this.addProtocolHandler("echo", (req: protocol.Request) => ({ this.addProtocolHandler("echo", (req: protocol.Request) => ({
response: req.arguments, response: req.arguments,
responseRequired: true responseRequired: true

View file

@ -190,7 +190,19 @@ namespace ts.projectSystem {
if (typingsInstaller === undefined) { if (typingsInstaller === undefined) {
typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host); typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
} }
return new TestSession(host, cancellationToken || server.nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ projectServiceEventHandler !== undefined, projectServiceEventHandler, throttleWaitMilliseconds); const opts: server.SessionOptions = {
host,
cancellationToken: cancellationToken || server.nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: nullLogger,
canUseEvents: projectServiceEventHandler !== undefined,
eventHandler: projectServiceEventHandler,
throttleWaitMilliseconds
};
return new TestSession(opts);
} }
export interface CreateProjectServiceParameters { export interface CreateProjectServiceParameters {
@ -205,7 +217,9 @@ namespace ts.projectSystem {
export class TestProjectService extends server.ProjectService { export class TestProjectService extends server.ProjectService {
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) { typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) {
super(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler); super({
host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler
});
} }
checkNumberOfProjects(count: { inferredProjects?: number, configuredProjects?: number, externalProjects?: number }) { checkNumberOfProjects(count: { inferredProjects?: number, configuredProjects?: number, externalProjects?: number }) {

View file

@ -273,6 +273,18 @@ namespace ts.server {
} }
} }
export interface ProjectServiceOptions {
host: ServerHost;
logger: Logger;
cancellationToken: HostCancellationToken;
useSingleInferredProject: boolean;
typingsInstaller: ITypingsInstaller;
eventHandler?: ProjectServiceEventHandler;
throttleWaitMilliseconds?: number;
globalPlugins?: string[];
pluginProbeLocations?: string[];
}
export class ProjectService { export class ProjectService {
public readonly typingsCache: TypingsCache; public readonly typingsCache: TypingsCache;
@ -320,19 +332,33 @@ namespace ts.server {
public lastDeletedFile: ScriptInfo; public lastDeletedFile: ScriptInfo;
constructor(public readonly host: ServerHost, public readonly host: ServerHost;
public readonly logger: Logger, public readonly logger: Logger;
public readonly cancellationToken: HostCancellationToken, public readonly cancellationToken: HostCancellationToken;
public readonly useSingleInferredProject: boolean, public readonly useSingleInferredProject: boolean;
readonly typingsInstaller: ITypingsInstaller = nullTypingsInstaller, public readonly typingsInstaller: ITypingsInstaller;
private readonly eventHandler?: ProjectServiceEventHandler, public readonly throttleWaitMilliseconds?: number;
public readonly throttleWaitMilliseconds?: number) { private readonly eventHandler?: ProjectServiceEventHandler;
Debug.assert(!!host.createHash, "'ServerHost.createHash' is required for ProjectService"); public readonly globalPlugins: ReadonlyArray<string>;
public readonly pluginProbeLocations: ReadonlyArray<string>;
this.toCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); constructor(opts: ProjectServiceOptions) {
this.host = opts.host;
this.logger = opts.logger;
this.cancellationToken = opts.cancellationToken;
this.useSingleInferredProject = opts.useSingleInferredProject;
this.typingsInstaller = opts.typingsInstaller || nullTypingsInstaller;
this.throttleWaitMilliseconds = opts.throttleWaitMilliseconds;
this.eventHandler = opts.eventHandler;
this.globalPlugins = opts.globalPlugins || emptyArray;
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService");
this.toCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
this.directoryWatchers = new DirectoryWatchers(this); this.directoryWatchers = new DirectoryWatchers(this);
this.throttledOperations = new ThrottledOperations(host); this.throttledOperations = new ThrottledOperations(this.host);
this.typingsInstaller.attach(this); this.typingsInstaller.attach(this);
@ -344,7 +370,7 @@ namespace ts.server {
extraFileExtensions: [] extraFileExtensions: []
}; };
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames, host.getCurrentDirectory()); this.documentRegistry = createDocumentRegistry(this.host.useCaseSensitiveFileNames, this.host.getCurrentDirectory());
} }
/* @internal */ /* @internal */

View file

@ -843,7 +843,7 @@ namespace ts.server {
/** Used for configured projects which may have multiple open roots */ /** Used for configured projects which may have multiple open roots */
openRefCount = 0; openRefCount = 0;
constructor(private configFileName: NormalizedPath, constructor(configFileName: NormalizedPath,
projectService: ProjectService, projectService: ProjectService,
documentRegistry: ts.DocumentRegistry, documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean, hasExplicitListOfFiles: boolean,
@ -863,9 +863,6 @@ namespace ts.server {
enablePlugins() { enablePlugins() {
const host = this.projectService.host; const host = this.projectService.host;
const options = this.getCompilerOptions(); const options = this.getCompilerOptions();
const log = (message: string) => {
this.projectService.logger.info(message);
};
if (!(options.plugins && options.plugins.length)) { if (!(options.plugins && options.plugins.length)) {
this.projectService.logger.info("No plugins exist"); this.projectService.logger.info("No plugins exist");
@ -878,13 +875,34 @@ namespace ts.server {
return; return;
} }
// Search our peer node_modules, then any globally-specified probe paths
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
const searchPaths = [combinePaths(host.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
// Enable tsconfig-specified plugins
for (const pluginConfigEntry of options.plugins) { for (const pluginConfigEntry of options.plugins) {
const searchPath = getDirectoryPath(this.configFileName); this.enablePlugin(pluginConfigEntry, searchPaths);
const resolvedModule = <PluginModuleFactory>Project.resolveModule(pluginConfigEntry.name, searchPath, host, log); }
// Enable global plugins with synthetic configuration entries
for (const globalPluginName of this.projectService.globalPlugins) {
// Provide global: true so plugins can detect why they can't find their config
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
}
}
private enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
const log = (message: string) => {
this.projectService.logger.info(message);
};
for (const searchPath of searchPaths) {
const resolvedModule = <PluginModuleFactory>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log);
if (resolvedModule) { if (resolvedModule) {
this.enableProxy(resolvedModule, pluginConfigEntry); this.enableProxy(resolvedModule, pluginConfigEntry);
return;
} }
} }
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name} anywhere in paths: ${searchPaths.join(",")}`);
} }
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) { private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {

View file

@ -3,6 +3,20 @@
/// <reference path="session.ts" /> /// <reference path="session.ts" />
namespace ts.server { namespace ts.server {
interface IOSessionOptions {
host: ServerHost;
cancellationToken: ServerCancellationToken;
canUseEvents: boolean;
installerEventPort: number;
useSingleInferredProject: boolean;
disableAutomaticTypingAcquisition: boolean;
globalTypingsCacheLocation: string;
logger: Logger;
typingSafeListLocation: string;
telemetryEnabled: boolean;
globalPlugins: string[];
pluginProbeLocations: string[];
}
const net: { const net: {
connect(options: { port: number }, onConnect?: () => void): NodeSocket connect(options: { port: number }, onConnect?: () => void): NodeSocket
@ -373,34 +387,25 @@ namespace ts.server {
} }
class IOSession extends Session { class IOSession extends Session {
constructor( constructor(options: IOSessionOptions) {
host: ServerHost, const { host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, canUseEvents } = options;
cancellationToken: ServerCancellationToken, const typingsInstaller = disableAutomaticTypingAcquisition
installerEventPort: number, ? undefined
canUseEvents: boolean, : new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, host.newLine);
useSingleInferredProject: boolean,
disableAutomaticTypingAcquisition: boolean,
globalTypingsCacheLocation: string,
typingSafeListLocation: string,
telemetryEnabled: boolean,
logger: server.Logger) {
const typingsInstaller = disableAutomaticTypingAcquisition
? undefined
: new NodeTypingsInstaller(telemetryEnabled, logger, host, installerEventPort, globalTypingsCacheLocation, typingSafeListLocation, host.newLine);
super( super({
host, host,
cancellationToken, cancellationToken,
useSingleInferredProject, useSingleInferredProject,
typingsInstaller || nullTypingsInstaller, typingsInstaller: typingsInstaller || nullTypingsInstaller,
Buffer.byteLength, byteLength: Buffer.byteLength,
process.hrtime, hrtime: process.hrtime,
logger, logger,
canUseEvents); canUseEvents});
if (telemetryEnabled && typingsInstaller) { if (telemetryEnabled && typingsInstaller) {
typingsInstaller.setTelemetrySender(this); typingsInstaller.setTelemetrySender(this);
} }
} }
exit() { exit() {
@ -735,21 +740,29 @@ namespace ts.server {
const typingSafeListLocation = findArgument("--typingSafeListLocation"); const typingSafeListLocation = findArgument("--typingSafeListLocation");
const globalPlugins = (findArgument("--globalPlugins") || "").split(",");
const pluginProbeLocations = (findArgument("--pluginProbeLocations") || "").split(",");
const useSingleInferredProject = hasArgument("--useSingleInferredProject"); const useSingleInferredProject = hasArgument("--useSingleInferredProject");
const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition"); const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition");
const telemetryEnabled = hasArgument(Arguments.EnableTelemetry); const telemetryEnabled = hasArgument(Arguments.EnableTelemetry);
const ioSession = new IOSession( const options: IOSessionOptions = {
sys, host: sys,
cancellationToken, cancellationToken,
eventPort, installerEventPort: eventPort,
/*canUseEvents*/ eventPort === undefined, canUseEvents: eventPort === undefined,
useSingleInferredProject, useSingleInferredProject,
disableAutomaticTypingAcquisition, disableAutomaticTypingAcquisition,
getGlobalTypingsCacheLocation(), globalTypingsCacheLocation: getGlobalTypingsCacheLocation(),
typingSafeListLocation, typingSafeListLocation,
telemetryEnabled, telemetryEnabled,
logger); logger,
globalPlugins,
pluginProbeLocations
};
const ioSession = new IOSession(options);
process.on("uncaughtException", function (err: Error) { process.on("uncaughtException", function (err: Error) {
ioSession.logError(err, "unknown"); ioSession.logError(err, "unknown");
}); });

View file

@ -319,6 +319,22 @@ namespace ts.server {
} }
} }
export interface SessionOptions {
host: ServerHost;
cancellationToken: ServerCancellationToken;
useSingleInferredProject: boolean;
typingsInstaller: ITypingsInstaller;
byteLength: (buf: string, encoding?: string) => number;
hrtime: (start?: number[]) => number[];
logger: Logger;
canUseEvents: boolean;
eventHandler?: ProjectServiceEventHandler;
throttleWaitMilliseconds?: number;
globalPlugins?: string[];
pluginProbeLocations?: string[];
}
export class Session implements EventSender { export class Session implements EventSender {
private readonly gcTimer: GcTimer; private readonly gcTimer: GcTimer;
protected projectService: ProjectService; protected projectService: ProjectService;
@ -327,22 +343,29 @@ namespace ts.server {
private currentRequestId: number; private currentRequestId: number;
private errorCheck: MultistepOperation; private errorCheck: MultistepOperation;
private eventHander: ProjectServiceEventHandler; private eventHandler: ProjectServiceEventHandler;
constructor( private host: ServerHost;
private host: ServerHost, private readonly cancellationToken: ServerCancellationToken;
private readonly cancellationToken: ServerCancellationToken, protected readonly typingsInstaller: ITypingsInstaller;
useSingleInferredProject: boolean, private byteLength: (buf: string, encoding?: string) => number;
protected readonly typingsInstaller: ITypingsInstaller, private hrtime: (start?: number[]) => number[];
private byteLength: (buf: string, encoding?: string) => number, protected logger: Logger;
private hrtime: (start?: number[]) => number[], private canUseEvents: boolean;
protected logger: Logger,
protected readonly canUseEvents: boolean,
eventHandler?: ProjectServiceEventHandler,
private readonly throttleWaitMilliseconds?: number) {
this.eventHander = canUseEvents constructor(opts: SessionOptions) {
? eventHandler || (event => this.defaultEventHandler(event)) this.host = opts.host;
this.cancellationToken = opts.cancellationToken;
this.typingsInstaller = opts.typingsInstaller;
this.byteLength = opts.byteLength;
this.hrtime = opts.hrtime;
this.logger = opts.logger;
this.canUseEvents = opts.canUseEvents;
const { throttleWaitMilliseconds } = opts;
this.eventHandler = this.canUseEvents
? opts.eventHandler || (event => this.defaultEventHandler(event))
: undefined; : undefined;
const multistepOperationHost: MultistepOperationHost = { const multistepOperationHost: MultistepOperationHost = {
@ -351,11 +374,22 @@ namespace ts.server {
getServerHost: () => this.host, getServerHost: () => this.host,
logError: (err, cmd) => this.logError(err, cmd), logError: (err, cmd) => this.logError(err, cmd),
sendRequestCompletedEvent: requestId => this.sendRequestCompletedEvent(requestId), sendRequestCompletedEvent: requestId => this.sendRequestCompletedEvent(requestId),
isCancellationRequested: () => cancellationToken.isCancellationRequested() isCancellationRequested: () => this.cancellationToken.isCancellationRequested()
}; };
this.errorCheck = new MultistepOperation(multistepOperationHost); this.errorCheck = new MultistepOperation(multistepOperationHost);
this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander, this.throttleWaitMilliseconds); const settings: ProjectServiceOptions = {
this.gcTimer = new GcTimer(host, /*delay*/ 7000, logger); host: this.host,
logger: this.logger,
cancellationToken: this.cancellationToken,
useSingleInferredProject: opts.useSingleInferredProject,
typingsInstaller: this.typingsInstaller,
throttleWaitMilliseconds,
eventHandler: this.eventHandler,
globalPlugins: opts.globalPlugins,
pluginProbeLocations: opts.pluginProbeLocations
};
this.projectService = new ProjectService(settings);
this.gcTimer = new GcTimer(this.host, /*delay*/ 7000, this.logger);
} }
private sendRequestCompletedEvent(requestId: number): void { private sendRequestCompletedEvent(requestId: number): void {
@ -947,8 +981,8 @@ namespace ts.server {
*/ */
private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind) { private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind) {
const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind); const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind);
if (this.eventHander) { if (this.eventHandler) {
this.eventHander({ this.eventHandler({
eventName: "configFileDiag", eventName: "configFileDiag",
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || [] } data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || [] }
}); });