[WIP] file watching

This commit is contained in:
Vladimir Matveev
2016-08-16 14:21:09 -07:00
parent dd70fdbb76
commit f0e1f9b108
8 changed files with 134 additions and 40 deletions

View File

@@ -27,7 +27,7 @@ namespace ts {
content: libFileContent
};
abstract class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
class TestTypingsInstaller extends server.typingsInstaller.TypingsInstaller implements server.ITypingsInstaller {
protected projectService: server.ProjectService;
constructor(readonly cachePath: string, readonly installTypingHost: server.ServerHost) {
super(cachePath, <Path>"");
@@ -44,6 +44,10 @@ namespace ts {
this.postInstallActions = [];
}
onProjectClosed(p: server.Project) {
}
attach(projectService: server.ProjectService) {
this.projectService = projectService;
}
@@ -66,7 +70,7 @@ namespace ts {
});
}
sendResponse(response: server.InstallTypingsResponse) {
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings) {
this.projectService.updateTypingsForProject(response);
}
@@ -1515,12 +1519,7 @@ namespace ts {
};
const host = createServerHost([file1, tsconfig, packageJson]);
class TypingInstaller extends TestTypingsInstaller {
constructor(host: server.ServerHost) {
super("/a/data/", host);
}
};
const installer = new TypingInstaller(host);
const installer = new TestTypingsInstaller("/a/data/", host);
const projectService = new server.ProjectService(host, nullLogger, nullCancellationToken, /*useSingleInferredProject*/ true, installer);
projectService.openClientFile(file1.path);

View File

@@ -206,14 +206,21 @@ namespace ts.server {
this.ensureInferredProjectsUpToDate();
}
updateTypingsForProject(response: InstallTypingsResponse): void {
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void {
const project = this.findProject(response.projectName);
if (!project) {
return;
}
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings);
project.setTypings(response.typings);
project.updateGraph();
switch (response.kind) {
case "set":
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings);
project.setTypings(response.typings);
project.updateGraph();
break;
case "invalidate":
this.typingsCache.invalidateCachedTypingsForProject(project);
break;
}
}
setCompilerOptionsForInferredProjects(compilerOptions: CompilerOptions): void {

View File

@@ -177,6 +177,10 @@ namespace ts.server {
this.installer.on("message", m => this.handleMessage(m));
}
onProjectClosed(p: Project): void {
this.installer.send({ projectName: p.getProjectName(), kind: "closeProject" });
}
enqueueInstallTypingsRequest(project: Project, typingOptions: TypingOptions): void {
const request = createInstallTypingsRequest(project, typingOptions);
if (this.logger.hasLevel(LogLevel.verbose)) {
@@ -185,7 +189,7 @@ namespace ts.server {
this.installer.send(request);
}
private handleMessage(response: InstallTypingsResponse) {
private handleMessage(response: SetTypings | InvalidateCachedTypings) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Received response: ${JSON.stringify(response)}`);
}

39
src/server/types.d.ts vendored
View File

@@ -3,15 +3,6 @@
/// <reference path="../services/jsTyping.ts"/>
declare namespace ts.server {
export interface InstallTypingsRequest {
readonly projectName: string;
readonly fileNames: string[];
readonly projectRootPath: ts.Path;
readonly typingOptions: ts.TypingOptions;
readonly compilerOptions: ts.CompilerOptions;
readonly cachePath?: string;
}
export interface CompressedData {
length: number;
compressionKind: string;
@@ -27,15 +18,43 @@ declare namespace ts.server {
gc?(): void;
}
export interface InstallTypingsResponse {
export interface TypingInstallerRequest {
readonly projectName: string;
readonly kind: "discover" | "closeProject";
}
export interface DiscoverTypings extends TypingInstallerRequest {
readonly fileNames: string[];
readonly projectRootPath: ts.Path;
readonly typingOptions: ts.TypingOptions;
readonly compilerOptions: ts.CompilerOptions;
readonly cachePath?: string;
readonly kind: "discover";
}
export interface CloseProject extends TypingInstallerRequest {
readonly kind: "closeProject";
}
export interface TypingInstallerResponse {
readonly projectName: string;
readonly kind: "set" | "invalidate";
}
export interface SetTypings extends TypingInstallerResponse {
readonly typingOptions: ts.TypingOptions;
readonly compilerOptions: ts.CompilerOptions;
readonly typings: string[];
readonly kind: "set";
}
export interface InvalidateCachedTypings extends TypingInstallerResponse {
readonly kind: "invalidate";
}
export interface InstallTypingHost extends JsTyping.TypingResolutionHost {
writeFile(path: string, content: string): void;
createDirectory(path: string): void;
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
}
}

View File

@@ -4,11 +4,13 @@ namespace ts.server {
export interface ITypingsInstaller {
enqueueInstallTypingsRequest(p: Project, typingOptions: TypingOptions): void;
attach(projectService: ProjectService): void;
onProjectClosed(p: Project): void;
}
export const nullTypingsInstaller: ITypingsInstaller = {
enqueueInstallTypingsRequest: () => {},
attach: (projectService: ProjectService) => {}
attach: (projectService: ProjectService) => {},
onProjectClosed: (p: Project) => {}
};
class TypingsCacheEntry {
@@ -95,6 +97,14 @@ namespace ts.server {
return entry ? entry.typings : emptyArray;
}
invalidateCachedTypingsForProject(project: Project) {
const typingOptions = getTypingOptionsForProjects(project);
if (!typingOptions.enableAutoDiscovery) {
return;
}
this.installer.enqueueInstallTypingsRequest(project, typingOptions);
}
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, newTypings: string[]) {
this.perProjectCache[projectName] = {
compilerOptions,
@@ -103,8 +113,9 @@ namespace ts.server {
};
}
deleteTypingsForProject(project: Project) {
onProjectClosed(project: Project) {
delete this.perProjectCache[project.getProjectName()];
this.installer.onProjectClosed(project);
}
}
}

View File

@@ -71,8 +71,14 @@ namespace ts.server.typingsInstaller {
this.log.writeLine(`Error when getting npm bin path: ${e}. Set bin path to ""`);
}
}
process.on("message", (req: InstallTypingsRequest) => {
this.install(req);
process.on("message", (req: DiscoverTypings | CloseProject) => {
switch (req.kind) {
case "discover":
this.install(req);
break;
case "closeProject":
this.closeProject(req);
}
})
}
@@ -96,7 +102,7 @@ namespace ts.server.typingsInstaller {
}
}
protected sendResponse(response: InstallTypingsResponse) {
protected sendResponse(response: SetTypings | InvalidateCachedTypings) {
if (this.log.isEnabled()) {
this.log.writeLine(`Sending response: ${JSON.stringify(response)}`)
}

View File

@@ -40,6 +40,8 @@ namespace ts.server.typingsInstaller {
private missingTypingsSet: Map<true> = createMap<true>();
private knownCachesSet: Map<true> = createMap<true>();
private projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
abstract readonly installTypingHost: InstallTypingHost;
constructor(readonly globalCachePath: string, readonly safeListPath: Path, protected readonly log = nullLog) {
@@ -66,7 +68,36 @@ namespace ts.server.typingsInstaller {
this.processCacheLocation(this.globalCachePath);
}
install(req: InstallTypingsRequest) {
closeProject(req: CloseProject) {
this.closeWatchers(req.projectName);
}
private closeWatchers(projectName: string): boolean {
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
}
const watchers = this.projectWatchers[projectName];
if (!watchers) {
if (this.log.isEnabled()) {
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
}
return false;
}
for (const w of watchers) {
w.close();
}
delete this.projectWatchers[projectName]
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
}
return true;
}
install(req: DiscoverTypings) {
if (!this.isTsdInstalled) {
if (this.log.isEnabled()) {
this.log.writeLine(`tsd is not installed, ignoring request...`);
@@ -100,10 +131,10 @@ namespace ts.server.typingsInstaller {
}
// respond with whatever cached typings we have now
this.sendResponse(this.createResponse(req, discoverTypingsResult.cachedTypingPaths));
this.sendResponse(this.createSetTypings(req, discoverTypingsResult.cachedTypingPaths));
// start watching files
this.watchFiles(discoverTypingsResult.filesToWatch);
this.watchFiles(req.projectRootPath, discoverTypingsResult.filesToWatch);
// install typings
this.installTypings(req, req.cachePath || this.globalCachePath, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
@@ -158,7 +189,7 @@ namespace ts.server.typingsInstaller {
this.knownCachesSet[cacheLocation] = true;
}
private installTypings(req: InstallTypingsRequest, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) {
private installTypings(req: DiscoverTypings, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) {
if (this.log.isEnabled()) {
this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`);
}
@@ -210,7 +241,7 @@ namespace ts.server.typingsInstaller {
}
}
this.sendResponse(this.createResponse(req, currentlyCachedTypings.concat(installedTypingFiles)));
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
});
}
@@ -224,22 +255,38 @@ namespace ts.server.typingsInstaller {
}
}
private watchFiles(files: string[]) {
// TODO: start watching files
private watchFiles(projectRootPath: string, files: string[]) {
if (!files.length) {
return;
}
const watchers: FileWatcher[] = [];
for (const file of files) {
const w = this.installTypingHost.watchFile(file, f => {
if (this.log.isEnabled()) {
this.log.writeLine(`FS notification for '${f}', sending 'clean' message for project '${projectRootPath}'`);
}
if (!this.closeWatchers(projectRootPath)) {
return;
}
this.sendResponse(<InvalidateCachedTypings>{ projectName: projectRootPath, kind: "invalidate" })
});
watchers.push(w);
}
}
private createResponse(request: InstallTypingsRequest, typings: string[]) {
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {
return {
projectName: request.projectName,
typingOptions: request.typingOptions,
compilerOptions: request.compilerOptions,
typings
typings,
kind: "set"
};
}
protected abstract isPackageInstalled(packageName: string): boolean;
protected abstract installPackage(packageName: string): boolean;
protected abstract sendResponse(response: InstallTypingsResponse): void;
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void;
protected abstract runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void;
}
}

View File

@@ -29,14 +29,15 @@ namespace ts.server {
export type Types = Err | Info | Perf;
}
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): InstallTypingsRequest {
export function createInstallTypingsRequest(project: Project, typingOptions: TypingOptions, cachePath?: string): DiscoverTypings {
return {
projectName: project.getProjectName(),
fileNames: project.getFileNames(),
compilerOptions: project.getCompilerOptions(),
typingOptions,
projectRootPath: <Path>(project.projectKind === ProjectKind.Inferred ? "" : getDirectoryPath(project.getProjectName())), // TODO: fixme
cachePath
cachePath,
kind: "discover"
};
}