diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 858282315804e..9f3da93b674c8 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -27,6 +27,7 @@ import { Diagnostic, directorySeparator, DirectoryStructureHost, + DirectoryWatcherCallback, DocumentPosition, DocumentPositionMapper, DocumentRegistry, @@ -37,6 +38,7 @@ import { FileExtensionInfo, fileExtensionIs, FileWatcher, + FileWatcherCallback, FileWatcherEventKind, find, flatMap, @@ -127,6 +129,7 @@ import { version, WatchDirectoryFlags, WatchFactory, + WatchFactoryHost, WatchLogLevel, WatchOptions, WatchType, @@ -193,6 +196,9 @@ export const ConfigFileDiagEvent = "configFileDiag"; export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState"; export const ProjectInfoTelemetryEvent = "projectInfo"; export const OpenFileInfoTelemetryEvent = "openFileInfo"; +export const CreateFileWatcherEvent: protocol.CreateFileWatcherEventName = "createFileWatcher"; +export const CreateDirectoryWatcherEvent: protocol.CreateDirectoryWatcherEventName = "createDirectoryWatcher"; +export const CloseFileWatcherEvent: protocol.CloseFileWatcherEventName = "closeFileWatcher"; const ensureProjectForOpenFileSchedule = "*ensureProjectForOpenFiles*"; export interface ProjectsUpdatedInBackgroundEvent { @@ -320,6 +326,21 @@ export interface OpenFileInfo { readonly checkJs: boolean; } +export interface CreateFileWatcherEvent { + readonly eventName: protocol.CreateFileWatcherEventName; + readonly data: protocol.CreateFileWatcherEventBody; +} + +export interface CreateDirectoryWatcherEvent { + readonly eventName: protocol.CreateDirectoryWatcherEventName; + readonly data: protocol.CreateDirectoryWatcherEventBody; +} + +export interface CloseFileWatcherEvent { + readonly eventName: protocol.CloseFileWatcherEventName; + readonly data: protocol.CloseFileWatcherEventBody; +} + export type ProjectServiceEvent = | LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent @@ -328,7 +349,10 @@ export type ProjectServiceEvent = | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent - | OpenFileInfoTelemetryEvent; + | OpenFileInfoTelemetryEvent + | CreateFileWatcherEvent + | CreateDirectoryWatcherEvent + | CloseFileWatcherEvent; export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void; @@ -583,6 +607,7 @@ export interface ProjectServiceOptions { useInferredProjectPerProjectRoot: boolean; typingsInstaller?: ITypingsInstaller; eventHandler?: ProjectServiceEventHandler; + canUseWatchEvents?: boolean; suppressDiagnosticEvents?: boolean; throttleWaitMilliseconds?: number; globalPlugins?: readonly string[]; @@ -857,6 +882,109 @@ function createProjectNameFactoryWithCounter(nameFactory: (counter: number) => s return () => nameFactory(nextId++); } +interface HostWatcherMap { + idToCallbacks: Map>; + pathToId: Map; +} + +function getHostWatcherMap(): HostWatcherMap { + return { idToCallbacks: new Map(), pathToId: new Map() }; +} + +function createWatchFactoryHostUsingWatchEvents(service: ProjectService, canUseWatchEvents: boolean | undefined): WatchFactoryHost | undefined { + if (!canUseWatchEvents || !service.eventHandler || !service.session) return undefined; + const watchedFiles = getHostWatcherMap(); + const watchedDirectories = getHostWatcherMap(); + const watchedDirectoriesRecursive = getHostWatcherMap(); + let ids = 1; + service.session.addProtocolHandler(protocol.CommandTypes.WatchChange, req => { + onWatchChange((req as protocol.WatchChangeRequest).arguments); + return { responseRequired: false }; + }); + return { + watchFile, + watchDirectory, + getCurrentDirectory: () => service.host.getCurrentDirectory(), + useCaseSensitiveFileNames: service.host.useCaseSensitiveFileNames, + }; + function watchFile(path: string, callback: FileWatcherCallback): FileWatcher { + return getOrCreateFileWatcher( + watchedFiles, + path, + callback, + id => ({ eventName: CreateFileWatcherEvent, data: { id, path } }), + ); + } + function watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher { + return getOrCreateFileWatcher( + recursive ? watchedDirectoriesRecursive : watchedDirectories, + path, + callback, + id => ({ eventName: CreateDirectoryWatcherEvent, data: { id, path, recursive: !!recursive } }), + ); + } + function getOrCreateFileWatcher( + { pathToId, idToCallbacks }: HostWatcherMap, + path: string, + callback: T, + event: (id: number) => CreateFileWatcherEvent | CreateDirectoryWatcherEvent, + ) { + const key = service.toPath(path); + let id = pathToId.get(key); + if (!id) pathToId.set(key, id = ids++); + let callbacks = idToCallbacks.get(id); + if (!callbacks) { + idToCallbacks.set(id, callbacks = new Set()); + // Add watcher + service.eventHandler!(event(id)); + } + callbacks.add(callback); + return { + close() { + const callbacks = idToCallbacks.get(id!); + if (!callbacks?.delete(callback)) return; + if (callbacks.size) return; + idToCallbacks.delete(id!); + pathToId.delete(key); + service.eventHandler!({ eventName: CloseFileWatcherEvent, data: { id: id! } }); + }, + }; + } + function onWatchChange({ id, path, eventType }: protocol.WatchChangeRequestArgs) { + // console.log(`typescript-vscode-watcher:: Invoke:: ${id}:: ${path}:: ${eventType}`); + onFileWatcherCallback(id, path, eventType); + onDirectoryWatcherCallback(watchedDirectories, id, path, eventType); + onDirectoryWatcherCallback(watchedDirectoriesRecursive, id, path, eventType); + } + + function onFileWatcherCallback( + id: number, + eventPath: string, + eventType: "create" | "delete" | "update", + ) { + watchedFiles.idToCallbacks.get(id)?.forEach(callback => { + const eventKind = eventType === "create" ? + FileWatcherEventKind.Created : + eventType === "delete" ? + FileWatcherEventKind.Deleted : + FileWatcherEventKind.Changed; + callback(eventPath, eventKind); + }); + } + + function onDirectoryWatcherCallback( + { idToCallbacks }: HostWatcherMap, + id: number, + eventPath: string, + eventType: "create" | "delete" | "update", + ) { + if (eventType === "update") return; + idToCallbacks.get(id)?.forEach(callback => { + callback(eventPath); + }); + } +} + export class ProjectService { /** @internal */ readonly typingsCache: TypingsCache; @@ -961,7 +1089,8 @@ export class ProjectService { public readonly typingsInstaller: ITypingsInstaller; private readonly globalCacheLocationDirectoryPath: Path | undefined; public readonly throttleWaitMilliseconds?: number; - private readonly eventHandler?: ProjectServiceEventHandler; + /** @internal */ + readonly eventHandler?: ProjectServiceEventHandler; private readonly suppressDiagnosticEvents?: boolean; public readonly globalPlugins: readonly string[]; @@ -1065,7 +1194,12 @@ export class ProjectService { watchFile: returnNoopFileWatcher, watchDirectory: returnNoopFileWatcher, } : - getWatchFactory(this.host, watchLogLevel, log, getDetailWatchInfo); + getWatchFactory( + createWatchFactoryHostUsingWatchEvents(this, opts.canUseWatchEvents) || this.host, + watchLogLevel, + log, + getDetailWatchInfo, + ); opts.incrementalVerifier?.(this); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 6d06376d196eb..daa1738650b29 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -173,6 +173,7 @@ export const enum CommandTypes { ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls", ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls", ProvideInlayHints = "provideInlayHints", + WatchChange = "watchChange", } /** @@ -1956,6 +1957,17 @@ export interface CloseRequest extends FileRequest { command: CommandTypes.Close; } +export interface WatchChangeRequest extends Request { + command: CommandTypes.WatchChange; + arguments: WatchChangeRequestArgs; +} + +export interface WatchChangeRequestArgs { + id: number; + path: string; + eventType: "create" | "delete" | "update"; +} + /** * Request to obtain the list of files that should be regenerated if target file is recompiled. * NOTE: this us query-only operation and does not generate any output on disk. @@ -3018,6 +3030,39 @@ export interface LargeFileReferencedEventBody { maxFileSize: number; } +export type CreateFileWatcherEventName = "createFileWatcher"; +export interface CreateFileWatcherEvent extends Event { + readonly event: CreateFileWatcherEventName; + readonly body: CreateFileWatcherEventBody; +} + +export interface CreateFileWatcherEventBody { + readonly id: number; + readonly path: string; +} + +export type CreateDirectoryWatcherEventName = "createDirectoryWatcher"; +export interface CreateDirectoryWatcherEvent extends Event { + readonly event: CreateDirectoryWatcherEventName; + readonly body: CreateDirectoryWatcherEventBody; +} + +export interface CreateDirectoryWatcherEventBody { + readonly id: number; + readonly path: string; + readonly recursive: boolean; +} + +export type CloseFileWatcherEventName = "closeFileWatcher"; +export interface CloseFileWatcherEvent extends Event { + readonly event: CloseFileWatcherEventName; + readonly body: CloseFileWatcherEventBody; +} + +export interface CloseFileWatcherEventBody { + readonly id: number; +} + /** @internal */ export type AnyEvent = | RequestCompletedEvent @@ -3029,7 +3074,10 @@ export type AnyEvent = | ProjectLoadingStartEvent | ProjectLoadingFinishEvent | SurveyReadyEvent - | LargeFileReferencedEvent; + | LargeFileReferencedEvent + | CreateFileWatcherEvent + | CreateDirectoryWatcherEvent + | CloseFileWatcherEvent; /** * Arguments for reload request. diff --git a/src/server/session.ts b/src/server/session.ts index 1da59c00e763a..42d1f52685103 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -139,11 +139,14 @@ import { WithMetadata, } from "./_namespaces/ts"; import { + CloseFileWatcherEvent, ConfigFileDiagEvent, ConfiguredProject, convertFormatOptions, convertScriptKindName, convertUserPreferences, + CreateDirectoryWatcherEvent, + CreateFileWatcherEvent, EmitResult, emptyArray, Errors, @@ -949,6 +952,7 @@ export interface SessionOptions { * If falsy, all events are suppressed. */ canUseEvents: boolean; + canUseWatchEvents?: boolean; eventHandler?: ProjectServiceEventHandler; /** Has no effect if eventHandler is also specified. */ suppressDiagnosticEvents?: boolean; @@ -1026,6 +1030,7 @@ export class Session implements EventSender { typesMapLocation: opts.typesMapLocation, serverMode: opts.serverMode, session: this, + canUseWatchEvents: opts.canUseWatchEvents, incrementalVerifier: opts.incrementalVerifier, }; this.projectService = new ProjectService(settings); @@ -1080,39 +1085,37 @@ export class Session implements EventSender { private defaultEventHandler(event: ProjectServiceEvent) { switch (event.eventName) { case ProjectsUpdatedInBackgroundEvent: - const { openFiles } = event.data; - this.projectsUpdatedInBackgroundEvent(openFiles); + this.projectsUpdatedInBackgroundEvent(event.data.openFiles); break; case ProjectLoadingStartEvent: - const { project, reason } = event.data; - this.event( - { projectName: project.getProjectName(), reason }, - ProjectLoadingStartEvent, - ); + this.event({ + projectName: event.data.project.getProjectName(), + reason: event.data.reason, + }, event.eventName); break; case ProjectLoadingFinishEvent: - const { project: finishProject } = event.data; - this.event({ projectName: finishProject.getProjectName() }, ProjectLoadingFinishEvent); + this.event({ + projectName: event.data.project.getProjectName(), + }, event.eventName); break; case LargeFileReferencedEvent: - const { file, fileSize, maxFileSize } = event.data; - this.event({ file, fileSize, maxFileSize }, LargeFileReferencedEvent); + case CreateFileWatcherEvent: + case CreateDirectoryWatcherEvent: + case CloseFileWatcherEvent: + this.event(event.data, event.eventName); break; case ConfigFileDiagEvent: - const { triggerFile, configFileName: configFile, diagnostics } = event.data; - const bakedDiags = map(diagnostics, diagnostic => formatDiagnosticToProtocol(diagnostic, /*includeFileName*/ true)); this.event({ - triggerFile, - configFile, - diagnostics: bakedDiags, - }, ConfigFileDiagEvent); + triggerFile: event.data.triggerFile, + configFile: event.data.configFileName, + diagnostics: map(event.data.diagnostics, diagnostic => formatDiagnosticToProtocol(diagnostic, /*includeFileName*/ true)), + }, event.eventName); break; case ProjectLanguageServiceStateEvent: { - const eventName: protocol.ProjectLanguageServiceStateEventName = ProjectLanguageServiceStateEvent; this.event({ projectName: event.data.project.getProjectName(), languageServiceEnabled: event.data.languageServiceEnabled, - }, eventName); + }, event.eventName); break; } case ProjectInfoTelemetryEvent: { diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts index 6c28643511d1f..2a5d2de2b383e 100644 --- a/src/testRunner/tests.ts +++ b/src/testRunner/tests.ts @@ -153,6 +153,7 @@ import "./unittests/tsserver/events/largeFileReferenced"; import "./unittests/tsserver/events/projectLanguageServiceState"; import "./unittests/tsserver/events/projectLoading"; import "./unittests/tsserver/events/projectUpdatedInBackground"; +import "./unittests/tsserver/events/watchEvents"; import "./unittests/tsserver/exportMapCache"; import "./unittests/tsserver/extends"; import "./unittests/tsserver/externalProjects"; diff --git a/src/testRunner/unittests/helpers/tsserver.ts b/src/testRunner/unittests/helpers/tsserver.ts index 2c04526b55e7e..c02774d812319 100644 --- a/src/testRunner/unittests/helpers/tsserver.ts +++ b/src/testRunner/unittests/helpers/tsserver.ts @@ -567,7 +567,7 @@ export function createSession(host: TestServerHost, opts: Partial) { +export function createSessionWithCustomEventHandler(host: TestServerHost, opts?: Partial, customAction?: (event: ts.server.ProjectServiceEvent) => void) { const session = createSession(host, { eventHandler, logger: createLoggerWithInMemoryLogs(host), ...opts }); return session; function eventHandler(event: ts.server.ProjectServiceEvent) { @@ -578,6 +578,9 @@ export function createSessionWithCustomEventHandler(host: TestServerHost, opts?: case ts.server.LargeFileReferencedEvent: case ts.server.ProjectInfoTelemetryEvent: case ts.server.OpenFileInfoTelemetryEvent: + case ts.server.CreateFileWatcherEvent: + case ts.server.CreateDirectoryWatcherEvent: + case ts.server.CloseFileWatcherEvent: break; // Convert project to project name case ts.server.ProjectLoadingStartEvent: @@ -593,6 +596,7 @@ export function createSessionWithCustomEventHandler(host: TestServerHost, opts?: ts.Debug.assertNever(event); } session.event(data, `CustomHandler::${event.eventName}`); + customAction?.(event); } } diff --git a/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts b/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts index eb96c9504e475..74d35ba8742c1 100644 --- a/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts +++ b/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts @@ -623,7 +623,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost, this.removeFileOrFolder(currentEntry); } - private hasWatchChanges?: boolean; + hasWatchChanges?: boolean; private createWatcher(map: MultiMap, path: Path, callback: T): FileWatcher { this.hasWatchChanges = true; map.add(path, callback); @@ -1115,7 +1115,7 @@ function diffFsEntry(baseline: string[], oldFsEntry: FSEntry | undefined, newFsE } } -function serializeMultiMap(baseline: string[], caption: string, multiMap: MultiMap, serialized: Map | undefined) { +export function serializeMultiMap(baseline: string[], caption: string, multiMap: MultiMap, serialized: Map | undefined) { let hasChange = diffMap(baseline, caption, multiMap, serialized, /*deleted*/ false); hasChange = diffMap(baseline, caption, serialized, multiMap, /*deleted*/ true) || hasChange; if (hasChange) { diff --git a/src/testRunner/unittests/tsserver/events/watchEvents.ts b/src/testRunner/unittests/tsserver/events/watchEvents.ts new file mode 100644 index 0000000000000..8c28c946480f0 --- /dev/null +++ b/src/testRunner/unittests/tsserver/events/watchEvents.ts @@ -0,0 +1,197 @@ +import * as ts from "../../../_namespaces/ts"; +import { + baselineTsserverLogs, + closeFilesForSession, + createLoggerWithInMemoryLogs, + createSession, + createSessionWithCustomEventHandler, + Logger, + openFilesForSession, + TestSession, +} from "../../helpers/tsserver"; +import { + createServerHost, + libFile, + serializeMultiMap, + TestServerHost, +} from "../../helpers/virtualFileSystemWithWatch"; + +describe("unittests:: tsserver:: events:: watchEvents", () => { + interface TestServerHostWithCustomWatch extends TestServerHost { + factoryData: { + watchedFiles: ts.MultiMap; + watchedDirectories: ts.MultiMap; + watchedDirectoriesRecursive: ts.MultiMap; + watchFile(data: ts.server.protocol.CreateFileWatcherEventBody): void; + watchDirectory(data: ts.server.protocol.CreateDirectoryWatcherEventBody): void; + closeWatcher(data: ts.server.protocol.CloseFileWatcherEventBody): void; + }; + } + + function createTestServerHostWithCustomWatch( + logger: Logger, + ) { + const idToClose = new Map void>(); + let serializedWatchedFiles: Map | undefined; + let serializedWatchedDirectories: Map | undefined; + let serializedWatchedDirectoriesRecursive: Map | undefined; + const host = logger.host as TestServerHostWithCustomWatch; + const originalSerializeWatches = host.serializeWatches; + host.serializeWatches = serializeWatches; + host.factoryData = { + watchedFiles: ts.createMultiMap(), + watchedDirectories: ts.createMultiMap(), + watchedDirectoriesRecursive: ts.createMultiMap(), + watchFile, + watchDirectory, + closeWatcher, + }; + return host; + + function watchFile(data: ts.server.protocol.CreateFileWatcherEventBody) { + logger.log(`Custom watchFile: ${data.id}: ${data.path}`); + ts.Debug.assert(!idToClose.has(data.id)); + host.factoryData.watchedFiles.add(data.path, data); + host.hasWatchChanges = true; + idToClose.set(data.id, () => { + logger.log(`Custom watchFile:: Close:: ${data.id}: ${data.path}`); + host.factoryData.watchedFiles.remove(data.path, data); + }); + } + + function watchDirectory(data: ts.server.protocol.CreateDirectoryWatcherEventBody) { + logger.log(`Custom watchDirectory: ${data.id}: ${data.path} ${data.recursive}`); + ts.Debug.assert(!idToClose.has(data.id)); + (data.recursive ? host.factoryData.watchedDirectoriesRecursive : host.factoryData.watchedDirectories).add(data.path, data); + host.hasWatchChanges = true; + idToClose.set(data.id, () => { + logger.log(`Custom watchDirectory:: Close:: ${data.id}: ${data.path} ${data.recursive}`); + (data.recursive ? host.factoryData.watchedDirectoriesRecursive : host.factoryData.watchedDirectories).remove(data.path, data); + }); + } + + function closeWatcher(data: ts.server.protocol.CloseFileWatcherEventBody) { + const close = idToClose.get(data.id); + if (close) { + idToClose.delete(data.id); + host.hasWatchChanges = true; + close(); + } + } + + function serializeWatches(baseline: string[] = []) { + const hasWatchChanges = host.hasWatchChanges; + originalSerializeWatches.call(host, baseline); + if (!hasWatchChanges) return baseline; + serializedWatchedFiles = serializeMultiMap(baseline, `Custom WatchedFiles`, host.factoryData.watchedFiles, serializedWatchedFiles); + serializedWatchedDirectories = serializeMultiMap(baseline, `Custom WatchedDirectories:Recursive`, host.factoryData.watchedDirectoriesRecursive, serializedWatchedDirectories); + serializedWatchedDirectoriesRecursive = serializeMultiMap(baseline, `Custom WatchedDirectories`, host.factoryData.watchedDirectories, serializedWatchedDirectoriesRecursive); + return baseline; + } + } + + function updateFileOnHost(session: TestSession, file: string, log: string) { + // Change b.ts + session.logger.log(log); + session.testhost.writeFile(file, session.testhost.readFile("/user/username/projects/myproject/a.ts")!); + session.testhost.runQueuedTimeoutCallbacks(); + } + + function addFile(session: TestSession, path: string) { + updateFileOnHost(session, path, "Add file"); + session.logger.log("Custom watch"); + (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchedDirectoriesRecursive.get("/user/username/projects/myproject")?.forEach(data => + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.WatchChange, + arguments: { id: data.id, path, eventType: "create" }, + }) + ); + session.testhost.runQueuedTimeoutCallbacks(); + } + + function changeFile(session: TestSession, path: string) { + updateFileOnHost(session, path, "Change File"); + session.logger.log("Custom watch"); + (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchedFiles.get(path)?.forEach(data => + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.WatchChange, + arguments: { id: data.id, path, eventType: "update" }, + }) + ); + session.testhost.runQueuedTimeoutCallbacks(); + } + + function setup() { + const inputHost = createServerHost({ + "/user/username/projects/myproject/tsconfig.json": "{}", + "/user/username/projects/myproject/a.ts": `export class a { prop = "hello"; foo() { return this.prop; } }`, + "/user/username/projects/myproject/b.ts": `export class b { prop = "hello"; foo() { return this.prop; } }`, + [libFile.path]: libFile.content, + }); + const logger = createLoggerWithInMemoryLogs(inputHost); + const host = createTestServerHostWithCustomWatch(logger); + return { host, logger }; + } + + it("canUseWatchEvents", () => { + const { host, logger } = setup(); + const session = createSessionWithCustomEventHandler(logger.host!, { canUseWatchEvents: true, logger }, handleWatchEvents); + openFilesForSession(["/user/username/projects/myproject/a.ts"], session); + + // Directory watcher + addFile(session, "/user/username/projects/myproject/c.ts"); + + // File Watcher + changeFile(session, "/user/username/projects/myproject/b.ts"); + + // Close watcher + openFilesForSession(["/user/username/projects/myproject/b.ts"], session); + + // Re watch + closeFilesForSession(["/user/username/projects/myproject/b.ts"], session); + + baselineTsserverLogs("events/watchEvents", `canUseWatchEvents`, session); + function handleWatchEvents(event: ts.server.ProjectServiceEvent) { + switch (event.eventName) { + case ts.server.CreateFileWatcherEvent: + host.factoryData.watchFile(event.data); + break; + case ts.server.CreateDirectoryWatcherEvent: + host.factoryData.watchDirectory(event.data); + break; + case ts.server.CloseFileWatcherEvent: + host.factoryData.closeWatcher(event.data); + break; + default: + break; + } + } + }); + + it("canUseWatchEvents without canUseEvents", () => { + const { logger } = setup(); + const session = createSession(logger.host!, { canUseEvents: false, logger }); + openFilesForSession(["/user/username/projects/myproject/a.ts"], session); + + // Directory watcher + addFile(session, "/user/username/projects/myproject/c.ts"); + + // File Watcher + changeFile(session, "/user/username/projects/myproject/b.ts"); + + // Close watcher + openFilesForSession(["/user/username/projects/myproject/b.ts"], session); + + // Re watch + closeFilesForSession(["/user/username/projects/myproject/b.ts"], session); + + // Shouldnt have watch change request + logger.msg = (s, type) => logger.info(`${type}:: ${s}`); + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.WatchChange, + arguments: { id: 1, path: "/user/username/projects/myproject/b.ts", eventType: "update" }, + }); + + baselineTsserverLogs("events/watchEvents", `canUseWatchEvents without canUseEvents`, session); + }); +}); diff --git a/src/tsserver/common.ts b/src/tsserver/common.ts index 0d55e49e2f13e..c21f3ba1dfd5c 100644 --- a/src/tsserver/common.ts +++ b/src/tsserver/common.ts @@ -30,6 +30,7 @@ export interface StartSessionOptions { useInferredProjectPerProjectRoot: SessionOptions["useInferredProjectPerProjectRoot"]; suppressDiagnosticEvents: SessionOptions["suppressDiagnosticEvents"]; noGetErrOnBackgroundUpdate: SessionOptions["noGetErrOnBackgroundUpdate"]; + canUseWatchEvents: SessionOptions["canUseWatchEvents"]; serverMode: SessionOptions["serverMode"]; } diff --git a/src/tsserver/server.ts b/src/tsserver/server.ts index 9ee38169e0057..e30bdafd3c4a1 100644 --- a/src/tsserver/server.ts +++ b/src/tsserver/server.ts @@ -57,6 +57,7 @@ function start({ args, logger, cancellationToken, serverMode, unknownServerMode, useInferredProjectPerProjectRoot: hasArgument("--useInferredProjectPerProjectRoot"), suppressDiagnosticEvents: hasArgument("--suppressDiagnosticEvents"), noGetErrOnBackgroundUpdate: hasArgument("--noGetErrOnBackgroundUpdate"), + canUseWatchEvents: hasArgument("--canUseWatchEvents"), serverMode, }, logger, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 88ce1a5067df1..98f1b33000391 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -171,6 +171,7 @@ declare namespace ts { ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls", ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls", ProvideInlayHints = "provideInlayHints", + WatchChange = "watchChange", } /** * A TypeScript Server message @@ -1504,6 +1505,15 @@ declare namespace ts { interface CloseRequest extends FileRequest { command: CommandTypes.Close; } + interface WatchChangeRequest extends Request { + command: CommandTypes.WatchChange; + arguments: WatchChangeRequestArgs; + } + interface WatchChangeRequestArgs { + id: number; + path: string; + eventType: "create" | "delete" | "update"; + } /** * Request to obtain the list of files that should be regenerated if target file is recompiled. * NOTE: this us query-only operation and does not generate any output on disk. @@ -2421,6 +2431,33 @@ declare namespace ts { */ maxFileSize: number; } + type CreateFileWatcherEventName = "createFileWatcher"; + interface CreateFileWatcherEvent extends Event { + readonly event: CreateFileWatcherEventName; + readonly body: CreateFileWatcherEventBody; + } + interface CreateFileWatcherEventBody { + readonly id: number; + readonly path: string; + } + type CreateDirectoryWatcherEventName = "createDirectoryWatcher"; + interface CreateDirectoryWatcherEvent extends Event { + readonly event: CreateDirectoryWatcherEventName; + readonly body: CreateDirectoryWatcherEventBody; + } + interface CreateDirectoryWatcherEventBody { + readonly id: number; + readonly path: string; + readonly recursive: boolean; + } + type CloseFileWatcherEventName = "closeFileWatcher"; + interface CloseFileWatcherEvent extends Event { + readonly event: CloseFileWatcherEventName; + readonly body: CloseFileWatcherEventBody; + } + interface CloseFileWatcherEventBody { + readonly id: number; + } /** * Arguments for reload request. */ @@ -3528,6 +3565,21 @@ declare namespace ts { readonly eventName: typeof OpenFileInfoTelemetryEvent; readonly data: OpenFileInfoTelemetryEventData; } + const CreateFileWatcherEvent: protocol.CreateFileWatcherEventName; + interface CreateFileWatcherEvent { + readonly eventName: protocol.CreateFileWatcherEventName; + readonly data: protocol.CreateFileWatcherEventBody; + } + const CreateDirectoryWatcherEvent: protocol.CreateDirectoryWatcherEventName; + interface CreateDirectoryWatcherEvent { + readonly eventName: protocol.CreateDirectoryWatcherEventName; + readonly data: protocol.CreateDirectoryWatcherEventBody; + } + const CloseFileWatcherEvent: protocol.CloseFileWatcherEventName; + interface CloseFileWatcherEvent { + readonly eventName: protocol.CloseFileWatcherEventName; + readonly data: protocol.CloseFileWatcherEventBody; + } interface ProjectInfoTelemetryEventData { /** Cryptographically secure hash of project file location. */ readonly projectId: string; @@ -3575,7 +3627,7 @@ declare namespace ts { interface OpenFileInfo { readonly checkJs: boolean; } - type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ProjectLoadingStartEvent | ProjectLoadingFinishEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent; + type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ProjectLoadingStartEvent | ProjectLoadingFinishEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent | CreateFileWatcherEvent | CreateDirectoryWatcherEvent | CloseFileWatcherEvent; type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void; interface SafeList { [name: string]: { @@ -3609,6 +3661,7 @@ declare namespace ts { useInferredProjectPerProjectRoot: boolean; typingsInstaller?: ITypingsInstaller; eventHandler?: ProjectServiceEventHandler; + canUseWatchEvents?: boolean; suppressDiagnosticEvents?: boolean; throttleWaitMilliseconds?: number; globalPlugins?: readonly string[]; @@ -3680,7 +3733,6 @@ declare namespace ts { readonly typingsInstaller: ITypingsInstaller; private readonly globalCacheLocationDirectoryPath; readonly throttleWaitMilliseconds?: number; - private readonly eventHandler?; private readonly suppressDiagnosticEvents?; readonly globalPlugins: readonly string[]; readonly pluginProbeLocations: readonly string[]; @@ -3894,6 +3946,7 @@ declare namespace ts { * If falsy, all events are suppressed. */ canUseEvents: boolean; + canUseWatchEvents?: boolean; eventHandler?: ProjectServiceEventHandler; /** Has no effect if eventHandler is also specified. */ suppressDiagnosticEvents?: boolean; diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js new file mode 100644 index 0000000000000..4e9b6ceeb3f8d --- /dev/null +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js @@ -0,0 +1,357 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist +Before request +//// [/user/username/projects/myproject/tsconfig.json] +{} + +//// [/user/username/projects/myproject/a.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + +//// [/user/username/projects/myproject/b.ts] +export class b { prop = "hello"; foo() { return this.prop; } } + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/user/username/projects/myproject/a.ts" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] Search path: /user/username/projects/myproject +Info seq [hh:mm:ss:mss] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Creating configuration project /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Info seq [hh:mm:ss:mss] Config: /user/username/projects/myproject/tsconfig.json : { + "rootNames": [ + "/user/username/projects/myproject/a.ts", + "/user/username/projects/myproject/b.ts" + ], + "options": { + "configFilePath": "/user/username/projects/myproject/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (3) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }" + /user/username/projects/myproject/a.ts SVC-1-0 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/b.ts Text-1 "export class b { prop = \"hello\"; foo() { return this.prop; } }" + + + ../../../../a/lib/lib.d.ts + Default library for target 'es5' + a.ts + Matched by default include pattern '**/*' + b.ts + Matched by default include pattern '**/*' + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (3) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/user/username/projects/myproject/node_modules/@types: *new* + {"pollingInterval":500} +/user/username/projects/node_modules/@types: *new* + {"pollingInterval":500} + +FsWatches:: +/a/lib/lib.d.ts: *new* + {} +/user/username/projects/myproject/b.ts: *new* + {} +/user/username/projects/myproject/tsconfig.json: *new* + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: *new* + {} + +Add file +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /user/username/projects/myproject/c.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/c.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Before running Timeout callback:: count: 2 +1: /user/username/projects/myproject/tsconfig.json +2: *ensureProjectForOpenFiles* +//// [/user/username/projects/myproject/c.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + + +Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 2 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }" + /user/username/projects/myproject/a.ts SVC-1-0 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/b.ts Text-1 "export class b { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/c.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + + + ../../../../a/lib/lib.d.ts + Default library for target 'es5' + a.ts + Matched by default include pattern '**/*' + b.ts + Matched by default include pattern '**/*' + c.ts + Matched by default include pattern '**/*' + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Running: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Before ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] After ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +After running Timeout callback:: count: 0 + +PolledWatches:: +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} +/user/username/projects/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/a/lib/lib.d.ts: + {} +/user/username/projects/myproject/b.ts: + {} +/user/username/projects/myproject/c.ts: *new* + {} +/user/username/projects/myproject/tsconfig.json: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Custom watch +Before running Timeout callback:: count: 0 + +After running Timeout callback:: count: 0 + +Change File +Info seq [hh:mm:ss:mss] FileWatcher:: Triggered with /user/username/projects/myproject/b.ts 1:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/b.ts 1:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Before running Timeout callback:: count: 2 +3: /user/username/projects/myproject/tsconfig.json +4: *ensureProjectForOpenFiles* +//// [/user/username/projects/myproject/b.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + + +Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 3 structureChanged: false structureIsReused:: Completely Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }" + /user/username/projects/myproject/a.ts SVC-1-0 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/b.ts Text-2 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/c.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Running: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Before ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] After ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +After running Timeout callback:: count: 0 + +Custom watch +Before running Timeout callback:: count: 0 + +After running Timeout callback:: count: 0 + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/user/username/projects/myproject/b.ts" + }, + "seq": 2, + "type": "request" + } +Info seq [hh:mm:ss:mss] FileWatcher:: Close:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Search path: /user/username/projects/myproject +Info seq [hh:mm:ss:mss] For info: /user/username/projects/myproject/b.ts :: Config file name: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/b.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} +/user/username/projects/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/a/lib/lib.d.ts: + {} +/user/username/projects/myproject/c.ts: + {} +/user/username/projects/myproject/tsconfig.json: + {} + +FsWatches *deleted*:: +/user/username/projects/myproject/b.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "close", + "arguments": { + "file": "/user/username/projects/myproject/b.ts" + }, + "seq": 3, + "type": "request" + } +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} +/user/username/projects/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/a/lib/lib.d.ts: + {} +/user/username/projects/myproject/b.ts: *new* + {} +/user/username/projects/myproject/c.ts: + {} +/user/username/projects/myproject/tsconfig.json: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 1, + "path": "/user/username/projects/myproject/b.ts", + "eventType": "update" + }, + "seq": 4, + "type": "request" + } +Info seq [hh:mm:ss:mss] Err:: Unrecognized JSON command: + {"command":"watchChange","arguments":{"id":1,"path":"/user/username/projects/myproject/b.ts","eventType":"update"},"seq":4,"type":"request"} +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "unknown", + "request_seq": 4, + "success": false, + "performanceData": { + "updateGraphDurationMs": * + }, + "message": "Unrecognized JSON command: watchChange" + } +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js new file mode 100644 index 0000000000000..34a10b3434b73 --- /dev/null +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js @@ -0,0 +1,545 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist +Before request +//// [/user/username/projects/myproject/tsconfig.json] +{} + +//// [/user/username/projects/myproject/a.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + +//// [/user/username/projects/myproject/b.ts] +export class b { prop = "hello"; foo() { return this.prop; } } + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/user/username/projects/myproject/a.ts" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] Search path: /user/username/projects/myproject +Info seq [hh:mm:ss:mss] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Creating configuration project /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 1, + "path": "/user/username/projects/myproject/tsconfig.json" + } + } +Custom watchFile: 1: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::projectLoadingStart", + "body": { + "project": "/user/username/projects/myproject/tsconfig.json", + "reason": "Creating possible configured project for /user/username/projects/myproject/a.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /user/username/projects/myproject/tsconfig.json : { + "rootNames": [ + "/user/username/projects/myproject/a.ts", + "/user/username/projects/myproject/b.ts" + ], + "options": { + "configFilePath": "/user/username/projects/myproject/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createDirectoryWatcher", + "body": { + "id": 2, + "path": "/user/username/projects/myproject", + "recursive": true + } + } +Custom watchDirectory: 2: /user/username/projects/myproject true +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 3, + "path": "/user/username/projects/myproject/b.ts" + } + } +Custom watchFile: 3: /user/username/projects/myproject/b.ts +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 4, + "path": "/a/lib/lib.d.ts" + } + } +Custom watchFile: 4: /a/lib/lib.d.ts +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createDirectoryWatcher", + "body": { + "id": 5, + "path": "/user/username/projects/myproject/node_modules/@types", + "recursive": true + } + } +Custom watchDirectory: 5: /user/username/projects/myproject/node_modules/@types true +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createDirectoryWatcher", + "body": { + "id": 6, + "path": "/user/username/projects/node_modules/@types", + "recursive": true + } + } +Custom watchDirectory: 6: /user/username/projects/node_modules/@types true +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (3) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }" + /user/username/projects/myproject/a.ts SVC-1-0 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/b.ts Text-1 "export class b { prop = \"hello\"; foo() { return this.prop; } }" + + + ../../../../a/lib/lib.d.ts + Default library for target 'es5' + a.ts + Matched by default include pattern '**/*' + b.ts + Matched by default include pattern '**/*' + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::projectLoadingFinish", + "body": { + "project": "/user/username/projects/myproject/tsconfig.json" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::projectInfo", + "body": { + "projectId": "4a33d78ee40d836c4f4e64c59aed976628aea0013be9585c5ff171dfc41baf98", + "fileStats": { + "js": 0, + "jsSize": 0, + "jsx": 0, + "jsxSize": 0, + "ts": 2, + "tsSize": 124, + "tsx": 0, + "tsxSize": 0, + "dts": 1, + "dtsSize": 334, + "deferred": 0, + "deferredSize": 0 + }, + "compilerOptions": {}, + "typeAcquisition": { + "enable": false, + "include": false, + "exclude": false + }, + "extends": false, + "files": false, + "include": false, + "exclude": false, + "compileOnSave": false, + "configFileName": "tsconfig.json", + "projectType": "configured", + "languageServiceEnabled": true, + "version": "FakeVersion" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::configFileDiag", + "body": { + "configFileName": "/user/username/projects/myproject/tsconfig.json", + "diagnostics": [], + "triggerFile": "/user/username/projects/myproject/a.ts" + } + } +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (3) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Custom WatchedFiles:: +/a/lib/lib.d.ts: *new* + {"id":4,"path":"/a/lib/lib.d.ts"} +/user/username/projects/myproject/b.ts: *new* + {"id":3,"path":"/user/username/projects/myproject/b.ts"} +/user/username/projects/myproject/tsconfig.json: *new* + {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} + +Custom WatchedDirectories:Recursive:: +/user/username/projects/myproject: *new* + {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules/@types: *new* + {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} +/user/username/projects/node_modules/@types: *new* + {"id":6,"path":"/user/username/projects/node_modules/@types","recursive":true} + +Add file +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/c.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + + +After running Timeout callback:: count: 0 + +Custom watch +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 2, + "path": "/user/username/projects/myproject/c.ts", + "eventType": "create" + }, + "seq": 2, + "type": "request" + } +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /user/username/projects/myproject/c.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/c.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Before running Timeout callback:: count: 2 +1: /user/username/projects/myproject/tsconfig.json +2: *ensureProjectForOpenFiles* + +Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 7, + "path": "/user/username/projects/myproject/c.ts" + } + } +Custom watchFile: 7: /user/username/projects/myproject/c.ts +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 2 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }" + /user/username/projects/myproject/a.ts SVC-1-0 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/b.ts Text-1 "export class b { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/c.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + + + ../../../../a/lib/lib.d.ts + Default library for target 'es5' + a.ts + Matched by default include pattern '**/*' + b.ts + Matched by default include pattern '**/*' + c.ts + Matched by default include pattern '**/*' + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Running: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Before ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] After ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::projectsUpdatedInBackground", + "body": { + "openFiles": [ + "/user/username/projects/myproject/a.ts" + ] + } + } +After running Timeout callback:: count: 0 + +Custom WatchedFiles:: +/a/lib/lib.d.ts: + {"id":4,"path":"/a/lib/lib.d.ts"} +/user/username/projects/myproject/b.ts: + {"id":3,"path":"/user/username/projects/myproject/b.ts"} +/user/username/projects/myproject/c.ts: *new* + {"id":7,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/tsconfig.json: + {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} + +Custom WatchedDirectories:Recursive:: +/user/username/projects/myproject: + {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules/@types: + {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} +/user/username/projects/node_modules/@types: + {"id":6,"path":"/user/username/projects/node_modules/@types","recursive":true} + +Change File +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/b.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + + +After running Timeout callback:: count: 0 + +Custom watch +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 3, + "path": "/user/username/projects/myproject/b.ts", + "eventType": "update" + }, + "seq": 3, + "type": "request" + } +Info seq [hh:mm:ss:mss] FileWatcher:: Triggered with /user/username/projects/myproject/b.ts 1:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/b.ts 1:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Before running Timeout callback:: count: 2 +3: /user/username/projects/myproject/tsconfig.json +4: *ensureProjectForOpenFiles* + +Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 3 structureChanged: false structureIsReused:: Completely Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }" + /user/username/projects/myproject/a.ts SVC-1-0 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/b.ts Text-2 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/c.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Running: *ensureProjectForOpenFiles* +Info seq [hh:mm:ss:mss] Before ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] After ensureProjectForOpenFiles: +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::projectsUpdatedInBackground", + "body": { + "openFiles": [ + "/user/username/projects/myproject/a.ts" + ] + } + } +After running Timeout callback:: count: 0 + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/user/username/projects/myproject/b.ts" + }, + "seq": 4, + "type": "request" + } +Info seq [hh:mm:ss:mss] FileWatcher:: Close:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::closeFileWatcher", + "body": { + "id": 3 + } + } +Custom watchFile:: Close:: 3: /user/username/projects/myproject/b.ts +Info seq [hh:mm:ss:mss] Search path: /user/username/projects/myproject +Info seq [hh:mm:ss:mss] For info: /user/username/projects/myproject/b.ts :: Config file name: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/b.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Custom WatchedFiles:: +/a/lib/lib.d.ts: + {"id":4,"path":"/a/lib/lib.d.ts"} +/user/username/projects/myproject/c.ts: + {"id":7,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/tsconfig.json: + {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} + +Custom WatchedFiles *deleted*:: +/user/username/projects/myproject/b.ts: + {"id":3,"path":"/user/username/projects/myproject/b.ts"} + +Custom WatchedDirectories:Recursive:: +/user/username/projects/myproject: + {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules/@types: + {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} +/user/username/projects/node_modules/@types: + {"id":6,"path":"/user/username/projects/node_modules/@types","recursive":true} + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "close", + "arguments": { + "file": "/user/username/projects/myproject/b.ts" + }, + "seq": 5, + "type": "request" + } +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 8, + "path": "/user/username/projects/myproject/b.ts" + } + } +Custom watchFile: 8: /user/username/projects/myproject/b.ts +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Custom WatchedFiles:: +/a/lib/lib.d.ts: + {"id":4,"path":"/a/lib/lib.d.ts"} +/user/username/projects/myproject/b.ts: *new* + {"id":8,"path":"/user/username/projects/myproject/b.ts"} +/user/username/projects/myproject/c.ts: + {"id":7,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/tsconfig.json: + {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} + +Custom WatchedDirectories:Recursive:: +/user/username/projects/myproject: + {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules/@types: + {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} +/user/username/projects/node_modules/@types: + {"id":6,"path":"/user/username/projects/node_modules/@types","recursive":true}