From 6f58ab70f85019de08985899809c2e4c4c2fd8dc Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 26 Mar 2024 15:57:07 -0700 Subject: [PATCH 1/4] Add test for node_modules file --- .../unittests/tsserver/events/watchEvents.ts | 58 ++- .../canUseWatchEvents-without-canUseEvents.js | 254 ++++++++++- .../events/watchEvents/canUseWatchEvents.js | 428 ++++++++++++++++-- 3 files changed, 665 insertions(+), 75 deletions(-) diff --git a/src/testRunner/unittests/tsserver/events/watchEvents.ts b/src/testRunner/unittests/tsserver/events/watchEvents.ts index 0a6028dcda33d..17087d05444ae 100644 --- a/src/testRunner/unittests/tsserver/events/watchEvents.ts +++ b/src/testRunner/unittests/tsserver/events/watchEvents.ts @@ -89,38 +89,56 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { } } - function updateFileOnHost(session: TestSession, file: string, log: string) { + function updateFileOnHost(session: TestSession, file: string, log: string, content?: string) { // Change b.ts - session.logger.log(log); - session.host.writeFile(file, session.host.readFile("/user/username/projects/myproject/a.ts")!); + session.logger.log(`${log}: ${file}`); + if (content) session.host.appendFile(file, content); + else session.host.writeFile(file, session.host.readFile("/user/username/projects/myproject/a.ts")!); session.host.runQueuedTimeoutCallbacks(); } - function addFile(session: TestSession, path: string) { - updateFileOnHost(session, path, "Add file"); - session.logger.log("Custom watch"); + function invokeDirectoryWatcher(session: TestSession, dir: string, path: string, eventType: ts.server.protocol.WatchChangeRequestArgs["eventType"]) { + session.logger.log(`Custom watch:: ${dir} ${path} ${eventType}`); (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive.forEach( - "/user/username/projects/myproject", + dir, data => session.executeCommandSeq({ command: ts.server.protocol.CommandTypes.WatchChange, - arguments: { id: data.id, path, eventType: "create" }, + arguments: { id: data.id, path, eventType }, }), ); - session.host.runQueuedTimeoutCallbacks(); } - function changeFile(session: TestSession, path: string) { - updateFileOnHost(session, path, "Change File"); - session.logger.log("Custom watch"); + function invokeFileWatcher(session: TestSession, path: string, eventType: ts.server.protocol.WatchChangeRequestArgs["eventType"]) { + session.logger.log(`Custom watch:: ${path} ${eventType}`); (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.pollingWatches.forEach( path, data => session.executeCommandSeq({ command: ts.server.protocol.CommandTypes.WatchChange, - arguments: { id: data.id, path, eventType: "update" }, + arguments: { id: data.id, path, eventType }, }), ); + } + + function addFile(session: TestSession, path: string) { + updateFileOnHost(session, path, "Add file"); + invokeDirectoryWatcher(session, "/user/username/projects/myproject", path, "create"); + session.host.runQueuedTimeoutCallbacks(); + } + + function changeFile(session: TestSession, path: string, content?: string) { + updateFileOnHost(session, path, "Change File", content); + invokeFileWatcher(session, path, "update"); + invokeDirectoryWatcher(session, ts.getDirectoryPath(path), path, "update"); + session.host.runQueuedTimeoutCallbacks(); + } + + function npmInstall(session: TestSession) { + session.logger.log("update with npm install"); + session.host.appendFile("/user/username/projects/myproject/node_modules/something/index.d.ts", `export const y = 20;`); + session.host.runQueuedTimeoutCallbacks(); + invokeDirectoryWatcher(session, "/user/username/projects/myproject/node_modules", "/user/username/projects/myproject/node_modules/something/index.d.ts", "update"); session.host.runQueuedTimeoutCallbacks(); } @@ -129,6 +147,8 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { "/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; } }`, + "/user/username/projects/myproject/m.ts": `import { x } from "something"`, + "/user/username/projects/myproject/node_modules/something/index.d.ts": `export const x = 10;`, [libFile.path]: libFile.content, }); const logger = createLoggerWithInMemoryLogs(inputHost); @@ -153,6 +173,12 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { // Re watch closeFilesForSession(["/user/username/projects/myproject/b.ts"], session); + // Update c.ts + changeFile(session, "/user/username/projects/myproject/c.ts", `export const y = 20;`); + + // Update with npm install + npmInstall(session); + baselineTsserverLogs("events/watchEvents", `canUseWatchEvents`, session); function handleWatchEvents(event: ts.server.ProjectServiceEvent) { switch (event.eventName) { @@ -195,6 +221,12 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { arguments: { id: 1, path: "/user/username/projects/myproject/b.ts", eventType: "update" }, }); + // Update c.ts + changeFile(session, "/user/username/projects/myproject/c.ts", `export const y = 20;`); + + // Update with npm install + npmInstall(session); + baselineTsserverLogs("events/watchEvents", `canUseWatchEvents without canUseEvents`, session); }); }); diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js index f4967a6194cc0..6fccf2ba08729 100644 --- a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js @@ -10,6 +10,12 @@ export class a { prop = "hello"; foo() { return this.prop; } } //// [/user/username/projects/myproject/b.ts] export class b { prop = "hello"; foo() { return this.prop; } } +//// [/user/username/projects/myproject/m.ts] +import { x } from "something" + +//// [/user/username/projects/myproject/node_modules/something/index.d.ts] +export const x = 10; + //// [/a/lib/lib.d.ts] /// interface Boolean {} @@ -40,7 +46,8 @@ Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/project 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" + "/user/username/projects/myproject/b.ts", + "/user/username/projects/myproject/m.ts" ], "options": { "configFilePath": "/user/username/projects/myproject/tsconfig.json" @@ -49,18 +56,25 @@ Info seq [hh:mm:ss:mss] Config: /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] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/m.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] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache 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 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations 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 projectStateVersion: 1 projectProgramVersion: 0 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) +Info seq [hh:mm:ss:mss] Files (5) /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" ../../../../a/lib/lib.d.ts @@ -69,10 +83,14 @@ Info seq [hh:mm:ss:mss] Files (3) Matched by default include pattern '**/*' b.ts Matched by default include pattern '**/*' + node_modules/something/index.d.ts + Imported via "something" from file 'm.ts' + m.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] Files (5) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -95,12 +113,16 @@ FsWatches:: {} /user/username/projects/myproject/b.ts: *new* {} +/user/username/projects/myproject/m.ts: *new* + {} /user/username/projects/myproject/tsconfig.json: *new* {} FsWatchesRecursive:: /user/username/projects/myproject: *new* {} +/user/username/projects/myproject/node_modules: *new* + {} Projects:: /user/username/projects/myproject/tsconfig.json (Configured) *new* @@ -120,8 +142,16 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts *new* + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts *new* + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json -Add file +Add file: /user/username/projects/myproject/c.ts 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* @@ -148,10 +178,12 @@ Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/project 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 projectStateVersion: 2 projectProgramVersion: 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 (4) +Info seq [hh:mm:ss:mss] Files (6) /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" /user/username/projects/myproject/c.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" @@ -161,6 +193,10 @@ Info seq [hh:mm:ss:mss] Files (4) Matched by default include pattern '**/*' b.ts Matched by default include pattern '**/*' + node_modules/something/index.d.ts + Imported via "something" from file 'm.ts' + m.ts + Matched by default include pattern '**/*' c.ts Matched by default include pattern '**/*' @@ -168,7 +204,7 @@ 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -176,7 +212,7 @@ Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts Proje 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -197,12 +233,16 @@ FsWatches:: {} /user/username/projects/myproject/c.ts: *new* {} +/user/username/projects/myproject/m.ts: + {} /user/username/projects/myproject/tsconfig.json: {} FsWatchesRecursive:: /user/username/projects/myproject: {} +/user/username/projects/myproject/node_modules: + {} Projects:: /user/username/projects/myproject/tsconfig.json (Configured) *changed* @@ -227,13 +267,21 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json -Custom watch +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts create Before running Timeout callback:: count: 0 After running Timeout callback:: count: 0 -Change File +Change File: /user/username/projects/myproject/b.ts 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* @@ -273,22 +321,32 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json 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 projectStateVersion: 3 projectProgramVersion: 2 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) +Info seq [hh:mm:ss:mss] Files (6) /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" /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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -296,7 +354,7 @@ Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts Proje 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -328,8 +386,17 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json -Custom watch +Custom watch:: /user/username/projects/myproject/b.ts update +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts update Before running Timeout callback:: count: 0 After running Timeout callback:: count: 0 @@ -349,7 +416,7 @@ Info seq [hh:mm:ss:mss] FileWatcher:: Close:: WatchInfo: /user/username/project 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -374,6 +441,8 @@ FsWatches:: {} /user/username/projects/myproject/c.ts: {} +/user/username/projects/myproject/m.ts: + {} /user/username/projects/myproject/tsconfig.json: {} @@ -384,6 +453,8 @@ FsWatches *deleted*:: FsWatchesRecursive:: /user/username/projects/myproject: {} +/user/username/projects/myproject/node_modules: + {} ScriptInfos:: /a/lib/lib.d.ts @@ -403,6 +474,14 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json Before request @@ -417,7 +496,7 @@ Info seq [hh:mm:ss:mss] 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -442,12 +521,16 @@ FsWatches:: {} /user/username/projects/myproject/c.ts: {} +/user/username/projects/myproject/m.ts: + {} /user/username/projects/myproject/tsconfig.json: {} FsWatchesRecursive:: /user/username/projects/myproject: {} +/user/username/projects/myproject/node_modules: + {} ScriptInfos:: /a/lib/lib.d.ts @@ -467,6 +550,14 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json Before request @@ -509,3 +600,136 @@ Info seq [hh:mm:ss:mss] response: "responseRequired": false } After request + +Change File: /user/username/projects/myproject/c.ts +Info seq [hh:mm:ss:mss] FileWatcher:: Triggered with /user/username/projects/myproject/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.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/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info +Before running Timeout callback:: count: 2 +5: /user/username/projects/myproject/tsconfig.json +6: *ensureProjectForOpenFiles* +//// [/user/username/projects/myproject/c.ts] +export class a { prop = "hello"; foo() { return this.prop; } }export const y = 20; + + +Timeout callback:: count: 2 +5: /user/username/projects/myproject/tsconfig.json *new* +6: *ensureProjectForOpenFiles* *new* + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 4 *changed* + projectProgramVersion: 2 + dirty: true *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts *changed* + version: Text-1 + pendingReloadFromDisk: true *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +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 projectStateVersion: 4 projectProgramVersion: 2 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 (6) + /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" + /user/username/projects/myproject/c.ts Text-2 "export class a { prop = \"hello\"; foo() { return this.prop; } }export const y = 20;" + +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 (6) + +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 (6) + +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 + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 4 + projectProgramVersion: 2 + dirty: false *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts *changed* + version: Text-2 *changed* + pendingReloadFromDisk: false *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +Custom watch:: /user/username/projects/myproject/c.ts update +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts update +Before running Timeout callback:: count: 0 + +After running Timeout callback:: count: 0 + +update with npm install +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/node_modules/something/index.d.ts] +export const x = 10;export const y = 20; + + +After running Timeout callback:: count: 0 + +Custom watch:: /user/username/projects/myproject/node_modules /user/username/projects/myproject/node_modules/something/index.d.ts update +Before running Timeout callback:: count: 0 + +After running Timeout callback:: count: 0 diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js index b7bc70a6bb148..01c026bbf91b0 100644 --- a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js @@ -10,6 +10,12 @@ export class a { prop = "hello"; foo() { return this.prop; } } //// [/user/username/projects/myproject/b.ts] export class b { prop = "hello"; foo() { return this.prop; } } +//// [/user/username/projects/myproject/m.ts] +import { x } from "something" + +//// [/user/username/projects/myproject/node_modules/something/index.d.ts] +export const x = 10; + //// [/a/lib/lib.d.ts] /// interface Boolean {} @@ -61,7 +67,8 @@ Info seq [hh:mm:ss:mss] event: 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" + "/user/username/projects/myproject/b.ts", + "/user/username/projects/myproject/m.ts" ], "options": { "configFilePath": "/user/username/projects/myproject/tsconfig.json" @@ -93,7 +100,33 @@ Info seq [hh:mm:ss:mss] event: } } Custom watchFile: 3: /user/username/projects/myproject/b.ts +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/m.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": "/user/username/projects/myproject/m.ts" + } + } +Custom watchFile: 4: /user/username/projects/myproject/m.ts Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createDirectoryWatcher", + "body": { + "id": 5, + "path": "/user/username/projects/myproject/node_modules", + "recursive": true + } + } +Custom watchDirectory: 5: /user/username/projects/myproject/node_modules true +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache 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: { @@ -101,11 +134,13 @@ Info seq [hh:mm:ss:mss] event: "type": "event", "event": "CustomHandler::createFileWatcher", "body": { - "id": 4, + "id": 6, "path": "/a/lib/lib.d.ts" } } -Custom watchFile: 4: /a/lib/lib.d.ts +Custom watchFile: 6: /a/lib/lib.d.ts +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations 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: { @@ -113,12 +148,12 @@ Info seq [hh:mm:ss:mss] event: "type": "event", "event": "CustomHandler::createDirectoryWatcher", "body": { - "id": 5, + "id": 7, "path": "/user/username/projects/myproject/node_modules/@types", "recursive": true } } -Custom watchDirectory: 5: /user/username/projects/myproject/node_modules/@types true +Custom watchDirectory: 7: /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: @@ -127,19 +162,21 @@ Info seq [hh:mm:ss:mss] event: "type": "event", "event": "CustomHandler::createDirectoryWatcher", "body": { - "id": 6, + "id": 8, "path": "/user/username/projects/node_modules/@types", "recursive": true } } -Custom watchDirectory: 6: /user/username/projects/node_modules/@types true +Custom watchDirectory: 8: /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 projectStateVersion: 1 projectProgramVersion: 0 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) +Info seq [hh:mm:ss:mss] Files (5) /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" ../../../../a/lib/lib.d.ts @@ -148,6 +185,10 @@ Info seq [hh:mm:ss:mss] Files (3) Matched by default include pattern '**/*' b.ts Matched by default include pattern '**/*' + node_modules/something/index.d.ts + Imported via "something" from file 'm.ts' + m.ts + Matched by default include pattern '**/*' Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] event: @@ -171,12 +212,12 @@ Info seq [hh:mm:ss:mss] event: "jsSize": 0, "jsx": 0, "jsxSize": 0, - "ts": 2, - "tsSize": 124, + "ts": 3, + "tsSize": 153, "tsx": 0, "tsxSize": 0, - "dts": 1, - "dtsSize": 334, + "dts": 2, + "dtsSize": 354, "deferred": 0, "deferredSize": 0 }, @@ -209,7 +250,7 @@ Info seq [hh:mm:ss:mss] event: } } 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] Files (5) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -223,19 +264,23 @@ After request Custom WatchedFiles:: /a/lib/lib.d.ts: *new* - {"id":4,"path":"/a/lib/lib.d.ts"} + {"id":6,"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/m.ts: *new* + {"id":4,"path":"/user/username/projects/myproject/m.ts"} /user/username/projects/myproject/tsconfig.json: *new* {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: *new* {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules: *new* + {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: *new* - {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"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} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} Projects:: /user/username/projects/myproject/tsconfig.json (Configured) *new* @@ -255,8 +300,16 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts *new* + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts *new* + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json -Add file +Add file: /user/username/projects/myproject/c.ts Before running Timeout callback:: count: 0 //// [/user/username/projects/myproject/c.ts] export class a { prop = "hello"; foo() { return this.prop; } } @@ -264,7 +317,7 @@ export class a { prop = "hello"; foo() { return this.prop; } } After running Timeout callback:: count: 0 -Custom watch +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts create Before request Info seq [hh:mm:ss:mss] request: @@ -310,18 +363,20 @@ Info seq [hh:mm:ss:mss] event: "type": "event", "event": "CustomHandler::createFileWatcher", "body": { - "id": 7, + "id": 9, "path": "/user/username/projects/myproject/c.ts" } } -Custom watchFile: 7: /user/username/projects/myproject/c.ts +Custom watchFile: 9: /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 projectStateVersion: 2 projectProgramVersion: 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 (4) +Info seq [hh:mm:ss:mss] Files (6) /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" /user/username/projects/myproject/c.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" @@ -331,6 +386,10 @@ Info seq [hh:mm:ss:mss] Files (4) Matched by default include pattern '**/*' b.ts Matched by default include pattern '**/*' + node_modules/something/index.d.ts + Imported via "something" from file 'm.ts' + m.ts + Matched by default include pattern '**/*' c.ts Matched by default include pattern '**/*' @@ -338,7 +397,7 @@ 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -346,7 +405,7 @@ Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts Proje 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -367,21 +426,25 @@ After running Timeout callback:: count: 0 Custom WatchedFiles:: /a/lib/lib.d.ts: - {"id":4,"path":"/a/lib/lib.d.ts"} + {"id":6,"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"} + {"id":9,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/m.ts: + {"id":4,"path":"/user/username/projects/myproject/m.ts"} /user/username/projects/myproject/tsconfig.json: {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules: + {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: - {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"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} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} Projects:: /user/username/projects/myproject/tsconfig.json (Configured) *changed* @@ -406,8 +469,16 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json -Change File +Change File: /user/username/projects/myproject/b.ts Before running Timeout callback:: count: 0 //// [/user/username/projects/myproject/b.ts] export class a { prop = "hello"; foo() { return this.prop; } } @@ -415,7 +486,7 @@ export class a { prop = "hello"; foo() { return this.prop; } } After running Timeout callback:: count: 0 -Custom watch +Custom watch:: /user/username/projects/myproject/b.ts update Before request Info seq [hh:mm:ss:mss] request: @@ -467,6 +538,34 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts update +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 2, + "path": "/user/username/projects/myproject/b.ts", + "eventType": "update" + }, + "seq": 4, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request Before running Timeout callback:: count: 2 3: /user/username/projects/myproject/tsconfig.json @@ -476,17 +575,19 @@ Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.jso 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 projectStateVersion: 3 projectProgramVersion: 2 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) +Info seq [hh:mm:ss:mss] Files (6) /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" /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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -494,7 +595,7 @@ Info seq [hh:mm:ss:mss] FileName: /user/username/projects/myproject/a.ts Proje 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -537,6 +638,14 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json Before request @@ -546,7 +655,7 @@ Info seq [hh:mm:ss:mss] request: "arguments": { "file": "/user/username/projects/myproject/b.ts" }, - "seq": 4, + "seq": 5, "type": "request" } Info seq [hh:mm:ss:mss] FileWatcher:: Close:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info @@ -563,7 +672,7 @@ 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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -579,9 +688,11 @@ After request Custom WatchedFiles:: /a/lib/lib.d.ts: - {"id":4,"path":"/a/lib/lib.d.ts"} + {"id":6,"path":"/a/lib/lib.d.ts"} /user/username/projects/myproject/c.ts: - {"id":7,"path":"/user/username/projects/myproject/c.ts"} + {"id":9,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/m.ts: + {"id":4,"path":"/user/username/projects/myproject/m.ts"} /user/username/projects/myproject/tsconfig.json: {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} @@ -592,10 +703,12 @@ Custom WatchedFiles *deleted*:: Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules: + {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: - {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"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} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} ScriptInfos:: /a/lib/lib.d.ts @@ -615,6 +728,14 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json Before request @@ -624,7 +745,7 @@ Info seq [hh:mm:ss:mss] request: "arguments": { "file": "/user/username/projects/myproject/b.ts" }, - "seq": 5, + "seq": 6, "type": "request" } Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/b.ts 500 undefined WatchType: Closed Script info @@ -634,13 +755,13 @@ Info seq [hh:mm:ss:mss] event: "type": "event", "event": "CustomHandler::createFileWatcher", "body": { - "id": 8, + "id": 10, "path": "/user/username/projects/myproject/b.ts" } } -Custom watchFile: 8: /user/username/projects/myproject/b.ts +Custom watchFile: 10: /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] Files (6) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: @@ -654,21 +775,25 @@ After request Custom WatchedFiles:: /a/lib/lib.d.ts: - {"id":4,"path":"/a/lib/lib.d.ts"} + {"id":6,"path":"/a/lib/lib.d.ts"} /user/username/projects/myproject/b.ts: *new* - {"id":8,"path":"/user/username/projects/myproject/b.ts"} + {"id":10,"path":"/user/username/projects/myproject/b.ts"} /user/username/projects/myproject/c.ts: - {"id":7,"path":"/user/username/projects/myproject/c.ts"} + {"id":9,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/m.ts: + {"id":4,"path":"/user/username/projects/myproject/m.ts"} /user/username/projects/myproject/tsconfig.json: {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: {"id":2,"path":"/user/username/projects/myproject","recursive":true} +/user/username/projects/myproject/node_modules: + {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: - {"id":5,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"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} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} ScriptInfos:: /a/lib/lib.d.ts @@ -688,3 +813,212 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +Change File: /user/username/projects/myproject/c.ts +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/c.ts] +export class a { prop = "hello"; foo() { return this.prop; } }export const y = 20; + + +After running Timeout callback:: count: 0 + +Custom watch:: /user/username/projects/myproject/c.ts update +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 9, + "path": "/user/username/projects/myproject/c.ts", + "eventType": "update" + }, + "seq": 7, + "type": "request" + } +Info seq [hh:mm:ss:mss] FileWatcher:: Triggered with /user/username/projects/myproject/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.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/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Timeout callback:: count: 2 +5: /user/username/projects/myproject/tsconfig.json *new* +6: *ensureProjectForOpenFiles* *new* + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 4 *changed* + projectProgramVersion: 2 + dirty: true *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts *changed* + version: Text-1 + pendingReloadFromDisk: true *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts update +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 2, + "path": "/user/username/projects/myproject/c.ts", + "eventType": "update" + }, + "seq": 8, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Before running Timeout callback:: count: 2 +5: /user/username/projects/myproject/tsconfig.json +6: *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 projectStateVersion: 4 projectProgramVersion: 2 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 (6) + /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/node_modules/something/index.d.ts Text-1 "export const x = 10;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" + /user/username/projects/myproject/c.ts Text-2 "export class a { prop = \"hello\"; foo() { return this.prop; } }export const y = 20;" + +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 (6) + +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 (6) + +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 + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 4 + projectProgramVersion: 2 + dirty: false *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts *changed* + version: Text-2 *changed* + pendingReloadFromDisk: false *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +update with npm install +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/node_modules/something/index.d.ts] +export const x = 10;export const y = 20; + + +After running Timeout callback:: count: 0 + +Custom watch:: /user/username/projects/myproject/node_modules /user/username/projects/myproject/node_modules/something/index.d.ts update +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": { + "id": 5, + "path": "/user/username/projects/myproject/node_modules/something/index.d.ts", + "eventType": "update" + }, + "seq": 9, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Before running Timeout callback:: count: 0 + +After running Timeout callback:: count: 0 From ddbd1ce42f2a5f844aca63eb8bd254920fce9c2a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 26 Mar 2024 15:43:00 -0700 Subject: [PATCH 2/4] Dont watch for update events if not watching node_modules directory --- src/server/editorServices.ts | 18 +- src/server/protocol.ts | 1 + .../unittests/tsserver/events/watchEvents.ts | 17 +- .../events/watchEvents/canUseWatchEvents.js | 191 ++++++++++++------ 4 files changed, 151 insertions(+), 76 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 5632d36430467..68e2102c283ad 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -933,7 +933,16 @@ function createWatchFactoryHostUsingWatchEvents(service: ProjectService, canUseW recursive ? watchedDirectoriesRecursive : watchedDirectories, path, callback, - id => ({ eventName: CreateDirectoryWatcherEvent, data: { id, path, recursive: !!recursive } }), + id => ({ + eventName: CreateDirectoryWatcherEvent, + data: { + id, + path, + recursive: !!recursive, + // Special case node_modules as we watch it for changes to closed script infos as well + ignoreUpdate: !path.endsWith("/node_modules") ? true : undefined, + }, + }), ); } function getOrCreateFileWatcher( @@ -964,10 +973,9 @@ function createWatchFactoryHostUsingWatchEvents(service: ProjectService, canUseW }; } 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); + onDirectoryWatcherCallback(watchedDirectories, id, path); + onDirectoryWatcherCallback(watchedDirectoriesRecursive, id, path); } function onFileWatcherCallback( @@ -989,9 +997,7 @@ function createWatchFactoryHostUsingWatchEvents(service: ProjectService, canUseW { idToCallbacks }: HostWatcherMap, id: number, eventPath: string, - eventType: "create" | "delete" | "update", ) { - if (eventType === "update") return; idToCallbacks.get(id)?.forEach(callback => { callback(eventPath); }); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 369d7b1589365..f9a432324b36b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2656,6 +2656,7 @@ export interface CreateDirectoryWatcherEventBody { readonly id: number; readonly path: string; readonly recursive: boolean; + readonly ignoreUpdate?: boolean; } export type CloseFileWatcherEventName = "closeFileWatcher"; diff --git a/src/testRunner/unittests/tsserver/events/watchEvents.ts b/src/testRunner/unittests/tsserver/events/watchEvents.ts index 17087d05444ae..166f687c12652 100644 --- a/src/testRunner/unittests/tsserver/events/watchEvents.ts +++ b/src/testRunner/unittests/tsserver/events/watchEvents.ts @@ -60,11 +60,11 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { } function watchDirectory(data: ts.server.protocol.CreateDirectoryWatcherEventBody) { - logger.log(`Custom watchDirectory: ${data.id}: ${data.path} ${data.recursive}`); + logger.log(`Custom watchDirectory: ${data.id}: ${data.path} ${data.recursive} ${data.ignoreUpdate}`); ts.Debug.assert(!idToClose.has(data.id)); const result = host.factoryData.watchUtils.fsWatch(data.path, data.recursive, data); idToClose.set(data.id, () => { - logger.log(`Custom watchDirectory:: Close:: ${data.id}: ${data.path} ${data.recursive}`); + logger.log(`Custom watchDirectory:: Close:: ${data.id}: ${data.path} ${data.recursive} ${data.ignoreUpdate}`); result.close(); }); } @@ -101,11 +101,14 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { session.logger.log(`Custom watch:: ${dir} ${path} ${eventType}`); (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive.forEach( dir, - data => - session.executeCommandSeq({ - command: ts.server.protocol.CommandTypes.WatchChange, - arguments: { id: data.id, path, eventType }, - }), + data => { + if (!data.ignoreUpdate || eventType !== "update") { + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.WatchChange, + arguments: { id: data.id, path, eventType }, + }); + } + }, ); } diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js index 01c026bbf91b0..30c3e4942041f 100644 --- a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js @@ -83,10 +83,11 @@ Info seq [hh:mm:ss:mss] event: "body": { "id": 2, "path": "/user/username/projects/myproject", - "recursive": true + "recursive": true, + "ignoreUpdate": true } } -Custom watchDirectory: 2: /user/username/projects/myproject true +Custom watchDirectory: 2: /user/username/projects/myproject true 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: @@ -125,7 +126,7 @@ Info seq [hh:mm:ss:mss] event: "recursive": true } } -Custom watchDirectory: 5: /user/username/projects/myproject/node_modules true +Custom watchDirectory: 5: /user/username/projects/myproject/node_modules true undefined Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache 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: @@ -150,10 +151,11 @@ Info seq [hh:mm:ss:mss] event: "body": { "id": 7, "path": "/user/username/projects/myproject/node_modules/@types", - "recursive": true + "recursive": true, + "ignoreUpdate": true } } -Custom watchDirectory: 7: /user/username/projects/myproject/node_modules/@types true +Custom watchDirectory: 7: /user/username/projects/myproject/node_modules/@types true 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: @@ -164,10 +166,11 @@ Info seq [hh:mm:ss:mss] event: "body": { "id": 8, "path": "/user/username/projects/node_modules/@types", - "recursive": true + "recursive": true, + "ignoreUpdate": true } } -Custom watchDirectory: 8: /user/username/projects/node_modules/@types true +Custom watchDirectory: 8: /user/username/projects/node_modules/@types true 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 projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) @@ -274,13 +277,13 @@ Custom WatchedFiles:: Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: *new* - {"id":2,"path":"/user/username/projects/myproject","recursive":true} + {"id":2,"path":"/user/username/projects/myproject","recursive":true,"ignoreUpdate":true} /user/username/projects/myproject/node_modules: *new* {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: *new* - {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true,"ignoreUpdate":true} /user/username/projects/node_modules/@types: *new* - {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true,"ignoreUpdate":true} Projects:: /user/username/projects/myproject/tsconfig.json (Configured) *new* @@ -438,13 +441,13 @@ Custom WatchedFiles:: Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: - {"id":2,"path":"/user/username/projects/myproject","recursive":true} + {"id":2,"path":"/user/username/projects/myproject","recursive":true,"ignoreUpdate":true} /user/username/projects/myproject/node_modules: {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: - {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true,"ignoreUpdate":true} /user/username/projects/node_modules/@types: - {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true,"ignoreUpdate":true} Projects:: /user/username/projects/myproject/tsconfig.json (Configured) *changed* @@ -548,25 +551,6 @@ ScriptInfos:: /user/username/projects/myproject/tsconfig.json Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts update -Before request - -Info seq [hh:mm:ss:mss] request: - { - "command": "watchChange", - "arguments": { - "id": 2, - "path": "/user/username/projects/myproject/b.ts", - "eventType": "update" - }, - "seq": 4, - "type": "request" - } -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* @@ -655,7 +639,7 @@ Info seq [hh:mm:ss:mss] request: "arguments": { "file": "/user/username/projects/myproject/b.ts" }, - "seq": 5, + "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 @@ -702,13 +686,13 @@ Custom WatchedFiles *deleted*:: Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: - {"id":2,"path":"/user/username/projects/myproject","recursive":true} + {"id":2,"path":"/user/username/projects/myproject","recursive":true,"ignoreUpdate":true} /user/username/projects/myproject/node_modules: {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: - {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true,"ignoreUpdate":true} /user/username/projects/node_modules/@types: - {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true,"ignoreUpdate":true} ScriptInfos:: /a/lib/lib.d.ts @@ -745,7 +729,7 @@ Info seq [hh:mm:ss:mss] request: "arguments": { "file": "/user/username/projects/myproject/b.ts" }, - "seq": 6, + "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 @@ -787,13 +771,13 @@ Custom WatchedFiles:: Custom WatchedDirectoriesRecursive:: /user/username/projects/myproject: - {"id":2,"path":"/user/username/projects/myproject","recursive":true} + {"id":2,"path":"/user/username/projects/myproject","recursive":true,"ignoreUpdate":true} /user/username/projects/myproject/node_modules: {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} /user/username/projects/myproject/node_modules/@types: - {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true} + {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true,"ignoreUpdate":true} /user/username/projects/node_modules/@types: - {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true} + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true,"ignoreUpdate":true} ScriptInfos:: /a/lib/lib.d.ts @@ -841,7 +825,7 @@ Info seq [hh:mm:ss:mss] request: "path": "/user/username/projects/myproject/c.ts", "eventType": "update" }, - "seq": 7, + "seq": 6, "type": "request" } Info seq [hh:mm:ss:mss] FileWatcher:: Triggered with /user/username/projects/myproject/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info @@ -892,25 +876,6 @@ ScriptInfos:: /user/username/projects/myproject/tsconfig.json Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts update -Before request - -Info seq [hh:mm:ss:mss] request: - { - "command": "watchChange", - "arguments": { - "id": 2, - "path": "/user/username/projects/myproject/c.ts", - "eventType": "update" - }, - "seq": 8, - "type": "request" - } -Info seq [hh:mm:ss:mss] response: - { - "responseRequired": false - } -After request - Before running Timeout callback:: count: 2 5: /user/username/projects/myproject/tsconfig.json 6: *ensureProjectForOpenFiles* @@ -1010,15 +975,115 @@ Info seq [hh:mm:ss:mss] request: "path": "/user/username/projects/myproject/node_modules/something/index.d.ts", "eventType": "update" }, - "seq": 9, + "seq": 7, "type": "request" } +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/something/index.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +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/node_modules/something/index.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/something/index.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations +Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.jsonFailedLookupInvalidation +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/something/index.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations Info seq [hh:mm:ss:mss] response: { "responseRequired": false } After request -Before running Timeout callback:: count: 0 +Timeout callback:: count: 3 +7: /user/username/projects/myproject/tsconfig.json *new* +8: *ensureProjectForOpenFiles* *new* +9: /user/username/projects/myproject/tsconfig.jsonFailedLookupInvalidation *new* -After running Timeout callback:: count: 0 +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 5 *changed* + projectProgramVersion: 2 + dirty: true *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts *changed* + version: Text-1 + pendingReloadFromDisk: true *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +Before running Timeout callback:: count: 3 +7: /user/username/projects/myproject/tsconfig.json +8: *ensureProjectForOpenFiles* +9: /user/username/projects/myproject/tsconfig.jsonFailedLookupInvalidation + +Info seq [hh:mm:ss:mss] Running: /user/username/projects/myproject/tsconfig.json +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one +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 projectStateVersion: 5 projectProgramVersion: 2 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (6) + /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/node_modules/something/index.d.ts Text-2 "export const x = 10;export const y = 20;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" + /user/username/projects/myproject/c.ts Text-2 "export class a { prop = \"hello\"; foo() { return this.prop; } }export const y = 20;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +After running Timeout callback:: count: 1 + +Timeout callback:: count: 1 +8: *ensureProjectForOpenFiles* *deleted* +9: /user/username/projects/myproject/tsconfig.jsonFailedLookupInvalidation *deleted* +10: *ensureProjectForOpenFiles* *new* + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 5 + projectProgramVersion: 3 *changed* + dirty: false *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts *changed* + version: Text-2 *changed* + pendingReloadFromDisk: false *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json From d1dcc7a22bd935465d96d634368bdb615a2631dc Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 27 Mar 2024 11:05:09 -0700 Subject: [PATCH 3/4] Change protocol to send events together for watchChange --- src/server/editorServices.ts | 42 ++- src/server/protocol.ts | 8 +- .../unittests/tsserver/events/watchEvents.ts | 135 +++++-- .../canUseWatchEvents-without-canUseEvents.js | 22 +- .../events/watchEvents/canUseWatchEvents.js | 329 +++++++++++++++++- 5 files changed, 463 insertions(+), 73 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 68e2102c283ad..168ecb744b67f 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -972,34 +972,32 @@ function createWatchFactoryHostUsingWatchEvents(service: ProjectService, canUseW }, }; } - function onWatchChange({ id, path, eventType }: protocol.WatchChangeRequestArgs) { - onFileWatcherCallback(id, path, eventType); - onDirectoryWatcherCallback(watchedDirectories, id, path); - onDirectoryWatcherCallback(watchedDirectoriesRecursive, id, path); + function onWatchChange(args: protocol.WatchChangeRequestArgs | readonly protocol.WatchChangeRequestArgs[]) { + if (isArray(args)) args.forEach(onWatchChangeRequestArgs); + else onWatchChangeRequestArgs(args); } - 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 onWatchChangeRequestArgs({ id, created, deleted, updated }: protocol.WatchChangeRequestArgs) { + onWatchEventType(id, created, FileWatcherEventKind.Created); + onWatchEventType(id, deleted, FileWatcherEventKind.Deleted); + onWatchEventType(id, updated, FileWatcherEventKind.Changed); + } + + function onWatchEventType(id: number, paths: readonly string[] | undefined, eventKind: FileWatcherEventKind) { + if (!paths?.length) return; + forEachCallback(watchedFiles, id, paths, (callback, eventPath) => callback(eventPath, eventKind)); + forEachCallback(watchedDirectories, id, paths, (callback, eventPath) => callback(eventPath)); + forEachCallback(watchedDirectoriesRecursive, id, paths, (callback, eventPath) => callback(eventPath)); } - function onDirectoryWatcherCallback( - { idToCallbacks }: HostWatcherMap, + function forEachCallback( + hostWatcherMap: HostWatcherMap, id: number, - eventPath: string, + eventPaths: readonly string[], + cb: (callback: T, eventPath: string) => void, ) { - idToCallbacks.get(id)?.forEach(callback => { - callback(eventPath); + hostWatcherMap.idToCallbacks.get(id)?.forEach(callback => { + eventPaths.forEach(eventPath => cb(callback, eventPath)); }); } } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index f9a432324b36b..8cf19bdb97616 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1849,13 +1849,13 @@ export interface CloseRequest extends FileRequest { export interface WatchChangeRequest extends Request { command: CommandTypes.WatchChange; - arguments: WatchChangeRequestArgs; + arguments: WatchChangeRequestArgs | readonly WatchChangeRequestArgs[]; } - export interface WatchChangeRequestArgs { id: number; - path: string; - eventType: "create" | "delete" | "update"; + created?: string[]; + deleted?: string[]; + updated?: string[]; } /** diff --git a/src/testRunner/unittests/tsserver/events/watchEvents.ts b/src/testRunner/unittests/tsserver/events/watchEvents.ts index 166f687c12652..88c6450a7ae52 100644 --- a/src/testRunner/unittests/tsserver/events/watchEvents.ts +++ b/src/testRunner/unittests/tsserver/events/watchEvents.ts @@ -4,6 +4,7 @@ import { } from "../../../../harness/tsserverLogger"; import { createWatchUtils, + Watches, WatchUtils, } from "../../../../harness/watchUtils"; import * as ts from "../../../_namespaces/ts"; @@ -97,43 +98,109 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { session.host.runQueuedTimeoutCallbacks(); } - function invokeDirectoryWatcher(session: TestSession, dir: string, path: string, eventType: ts.server.protocol.WatchChangeRequestArgs["eventType"]) { - session.logger.log(`Custom watch:: ${dir} ${path} ${eventType}`); - (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive.forEach( - dir, + function collectWatchChanges( + session: TestSession, + watches: Watches, + path: string, + eventPath: string, + eventType: "created" | "deleted" | "updated", + ignoreUpdate?: (data: T) => boolean, + ) { + session.logger.log(`Custom watch:: ${path} ${eventPath} ${eventType}`); + let result: ts.server.protocol.WatchChangeRequestArgs[] | undefined; + watches.forEach( + path, data => { - if (!data.ignoreUpdate || eventType !== "update") { - session.executeCommandSeq({ - command: ts.server.protocol.CommandTypes.WatchChange, - arguments: { id: data.id, path, eventType }, - }); + if (ignoreUpdate?.(data)) return; + switch (eventType) { + case "created": + (result ??= []).push({ id: data.id, created: [eventPath] }); + break; + case "deleted": + (result ??= []).push({ id: data.id, deleted: [eventPath] }); + break; + case "updated": + (result ??= []).push({ id: data.id, updated: [eventPath] }); + break; + default: + ts.Debug.assertNever(eventType); } }, ); + return result; } - function invokeFileWatcher(session: TestSession, path: string, eventType: ts.server.protocol.WatchChangeRequestArgs["eventType"]) { - session.logger.log(`Custom watch:: ${path} ${eventType}`); - (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.pollingWatches.forEach( - path, - data => - session.executeCommandSeq({ - command: ts.server.protocol.CommandTypes.WatchChange, - arguments: { id: data.id, path, eventType }, - }), + function collectDirectoryWatcherChanges( + session: TestSession, + dir: string, + eventPath: string, + eventType: "created" | "deleted" | "updated", + ) { + return collectWatchChanges( + session, + (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive, + dir, + eventPath, + eventType, + data => !!data.ignoreUpdate && eventType === "updated", + ); + } + + function collectFileWatcherChanges( + session: TestSession, + file: string, + eventType: "created" | "deleted" | "updated", + ) { + return collectWatchChanges( + session, + (session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.pollingWatches, + file, + file, + eventType, + ); + } + + function invokeWatchChange( + session: TestSession, + ...args: (ts.server.protocol.WatchChangeRequestArgs[] | undefined)[] + ) { + let result: Map | undefined; + args.forEach(arg => + arg?.forEach(value => { + result ??= new Map(); + const valueInResult = result.get(value.id); + if (!valueInResult) result.set(value.id, value); + else { + valueInResult.created = ts.combine(valueInResult.created, value.created); + valueInResult.deleted = ts.combine(valueInResult.deleted, value.deleted); + valueInResult.updated = ts.combine(valueInResult.updated, value.updated); + } + }) ); + if (result) { + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.WatchChange, + arguments: ts.singleOrMany(ts.arrayFrom(result.values())), + }); + } } function addFile(session: TestSession, path: string) { updateFileOnHost(session, path, "Add file"); - invokeDirectoryWatcher(session, "/user/username/projects/myproject", path, "create"); + invokeWatchChange( + session, + collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", path, "created"), + ); session.host.runQueuedTimeoutCallbacks(); } function changeFile(session: TestSession, path: string, content?: string) { updateFileOnHost(session, path, "Change File", content); - invokeFileWatcher(session, path, "update"); - invokeDirectoryWatcher(session, ts.getDirectoryPath(path), path, "update"); + invokeWatchChange( + session, + collectFileWatcherChanges(session, path, "updated"), + collectDirectoryWatcherChanges(session, ts.getDirectoryPath(path), path, "updated"), + ); session.host.runQueuedTimeoutCallbacks(); } @@ -141,7 +208,15 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { session.logger.log("update with npm install"); session.host.appendFile("/user/username/projects/myproject/node_modules/something/index.d.ts", `export const y = 20;`); session.host.runQueuedTimeoutCallbacks(); - invokeDirectoryWatcher(session, "/user/username/projects/myproject/node_modules", "/user/username/projects/myproject/node_modules/something/index.d.ts", "update"); + invokeWatchChange( + session, + collectDirectoryWatcherChanges( + session, + "/user/username/projects/myproject/node_modules", + "/user/username/projects/myproject/node_modules/something/index.d.ts", + "updated", + ), + ); session.host.runQueuedTimeoutCallbacks(); } @@ -181,6 +256,20 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { // Update with npm install npmInstall(session); + host.runQueuedTimeoutCallbacks(); + + // Add and change multiple files - combine and send multiple requests together + updateFileOnHost(session, "/user/username/projects/myproject/d.ts", "Add file"); + updateFileOnHost(session, "/user/username/projects/myproject/c.ts", "Change File", `export const z = 30;`); + updateFileOnHost(session, "/user/username/projects/myproject/e.ts", "Add File"); + invokeWatchChange( + session, + collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", "/user/username/projects/myproject/d.ts", "created"), + collectFileWatcherChanges(session, "/user/username/projects/myproject/c.ts", "updated"), + collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", "/user/username/projects/myproject/c.ts", "updated"), + collectDirectoryWatcherChanges(session, "/user/username/projects/myproject", "/user/username/projects/myproject/e.ts", "created"), + ); + session.host.runQueuedTimeoutCallbacks(); baselineTsserverLogs("events/watchEvents", `canUseWatchEvents`, session); function handleWatchEvents(event: ts.server.ProjectServiceEvent) { @@ -221,7 +310,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => { 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" }, + arguments: { id: 1, updated: ["/user/username/projects/myproject/b.ts"] }, }); // Update c.ts diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js index 6fccf2ba08729..103fa17f1687b 100644 --- a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents-without-canUseEvents.js @@ -276,7 +276,7 @@ ScriptInfos:: containingProjects: 1 /user/username/projects/myproject/tsconfig.json -Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts create +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts created Before running Timeout callback:: count: 0 After running Timeout callback:: count: 0 @@ -395,8 +395,8 @@ ScriptInfos:: containingProjects: 1 /user/username/projects/myproject/tsconfig.json -Custom watch:: /user/username/projects/myproject/b.ts update -Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts update +Custom watch:: /user/username/projects/myproject/b.ts /user/username/projects/myproject/b.ts updated +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts updated Before running Timeout callback:: count: 0 After running Timeout callback:: count: 0 @@ -566,8 +566,9 @@ Info seq [hh:mm:ss:mss] request: "command": "watchChange", "arguments": { "id": 1, - "path": "/user/username/projects/myproject/b.ts", - "eventType": "update" + "updated": [ + "/user/username/projects/myproject/b.ts" + ] }, "seq": 4, "type": "request" @@ -577,8 +578,9 @@ Info seq [hh:mm:ss:mss] Err:: Unrecognized JSON command: "command": "watchChange", "arguments": { "id": 1, - "path": "/user/username/projects/myproject/b.ts", - "eventType": "update" + "updated": [ + "/user/username/projects/myproject/b.ts" + ] }, "seq": 4, "type": "request" @@ -715,8 +717,8 @@ ScriptInfos:: containingProjects: 1 /user/username/projects/myproject/tsconfig.json -Custom watch:: /user/username/projects/myproject/c.ts update -Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts update +Custom watch:: /user/username/projects/myproject/c.ts /user/username/projects/myproject/c.ts updated +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts updated Before running Timeout callback:: count: 0 After running Timeout callback:: count: 0 @@ -729,7 +731,7 @@ export const x = 10;export const y = 20; After running Timeout callback:: count: 0 -Custom watch:: /user/username/projects/myproject/node_modules /user/username/projects/myproject/node_modules/something/index.d.ts update +Custom watch:: /user/username/projects/myproject/node_modules /user/username/projects/myproject/node_modules/something/index.d.ts updated Before running Timeout callback:: count: 0 After running Timeout callback:: count: 0 diff --git a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js index 30c3e4942041f..a70a15d02f3e5 100644 --- a/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js +++ b/tests/baselines/reference/tsserver/events/watchEvents/canUseWatchEvents.js @@ -320,7 +320,7 @@ export class a { prop = "hello"; foo() { return this.prop; } } After running Timeout callback:: count: 0 -Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts create +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts created Before request Info seq [hh:mm:ss:mss] request: @@ -328,8 +328,9 @@ Info seq [hh:mm:ss:mss] request: "command": "watchChange", "arguments": { "id": 2, - "path": "/user/username/projects/myproject/c.ts", - "eventType": "create" + "created": [ + "/user/username/projects/myproject/c.ts" + ] }, "seq": 2, "type": "request" @@ -489,7 +490,8 @@ export class a { prop = "hello"; foo() { return this.prop; } } After running Timeout callback:: count: 0 -Custom watch:: /user/username/projects/myproject/b.ts update +Custom watch:: /user/username/projects/myproject/b.ts /user/username/projects/myproject/b.ts updated +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts updated Before request Info seq [hh:mm:ss:mss] request: @@ -497,8 +499,9 @@ Info seq [hh:mm:ss:mss] request: "command": "watchChange", "arguments": { "id": 3, - "path": "/user/username/projects/myproject/b.ts", - "eventType": "update" + "updated": [ + "/user/username/projects/myproject/b.ts" + ] }, "seq": 3, "type": "request" @@ -550,7 +553,6 @@ ScriptInfos:: containingProjects: 1 /user/username/projects/myproject/tsconfig.json -Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/b.ts update Before running Timeout callback:: count: 2 3: /user/username/projects/myproject/tsconfig.json 4: *ensureProjectForOpenFiles* @@ -814,7 +816,8 @@ export class a { prop = "hello"; foo() { return this.prop; } }export const y = 2 After running Timeout callback:: count: 0 -Custom watch:: /user/username/projects/myproject/c.ts update +Custom watch:: /user/username/projects/myproject/c.ts /user/username/projects/myproject/c.ts updated +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts updated Before request Info seq [hh:mm:ss:mss] request: @@ -822,8 +825,9 @@ Info seq [hh:mm:ss:mss] request: "command": "watchChange", "arguments": { "id": 9, - "path": "/user/username/projects/myproject/c.ts", - "eventType": "update" + "updated": [ + "/user/username/projects/myproject/c.ts" + ] }, "seq": 6, "type": "request" @@ -875,7 +879,6 @@ ScriptInfos:: containingProjects: 1 /user/username/projects/myproject/tsconfig.json -Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts update Before running Timeout callback:: count: 2 5: /user/username/projects/myproject/tsconfig.json 6: *ensureProjectForOpenFiles* @@ -964,7 +967,7 @@ export const x = 10;export const y = 20; After running Timeout callback:: count: 0 -Custom watch:: /user/username/projects/myproject/node_modules /user/username/projects/myproject/node_modules/something/index.d.ts update +Custom watch:: /user/username/projects/myproject/node_modules /user/username/projects/myproject/node_modules/something/index.d.ts updated Before request Info seq [hh:mm:ss:mss] request: @@ -972,8 +975,9 @@ Info seq [hh:mm:ss:mss] request: "command": "watchChange", "arguments": { "id": 5, - "path": "/user/username/projects/myproject/node_modules/something/index.d.ts", - "eventType": "update" + "updated": [ + "/user/username/projects/myproject/node_modules/something/index.d.ts" + ] }, "seq": 7, "type": "request" @@ -1087,3 +1091,300 @@ ScriptInfos:: pendingReloadFromDisk: false *changed* containingProjects: 1 /user/username/projects/myproject/tsconfig.json + +Before running Timeout callback:: count: 1 +10: *ensureProjectForOpenFiles* + +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 (6) + +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 (6) + +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 + +Add file: /user/username/projects/myproject/d.ts +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/d.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + + +After running Timeout callback:: count: 0 + +Change File: /user/username/projects/myproject/c.ts +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/c.ts] +export class a { prop = "hello"; foo() { return this.prop; } }export const y = 20;export const z = 30; + + +After running Timeout callback:: count: 0 + +Add File: /user/username/projects/myproject/e.ts +Before running Timeout callback:: count: 0 +//// [/user/username/projects/myproject/e.ts] +export class a { prop = "hello"; foo() { return this.prop; } } + + +After running Timeout callback:: count: 0 + +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/d.ts created +Custom watch:: /user/username/projects/myproject/c.ts /user/username/projects/myproject/c.ts updated +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/c.ts updated +Custom watch:: /user/username/projects/myproject /user/username/projects/myproject/e.ts created +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "watchChange", + "arguments": [ + { + "id": 2, + "created": [ + "/user/username/projects/myproject/d.ts", + "/user/username/projects/myproject/e.ts" + ] + }, + { + "id": 9, + "updated": [ + "/user/username/projects/myproject/c.ts" + ] + } + ], + "seq": 8, + "type": "request" + } +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Triggered with /user/username/projects/myproject/d.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/d.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] DirectoryWatcher:: Triggered with /user/username/projects/myproject/e.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, Cancelled earlier one +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/e.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] FileWatcher:: Triggered with /user/username/projects/myproject/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] Scheduled: /user/username/projects/myproject/tsconfig.json, Cancelled earlier one +Info seq [hh:mm:ss:mss] Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one +Info seq [hh:mm:ss:mss] Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/c.ts 1:: WatchInfo: /user/username/projects/myproject/c.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Timeout callback:: count: 2 +15: /user/username/projects/myproject/tsconfig.json *new* +16: *ensureProjectForOpenFiles* *new* + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 6 *changed* + projectProgramVersion: 3 + dirty: true *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts *changed* + version: Text-2 + pendingReloadFromDisk: true *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json + +Before running Timeout callback:: count: 2 +15: /user/username/projects/myproject/tsconfig.json +16: *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/d.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 11, + "path": "/user/username/projects/myproject/d.ts" + } + } +Custom watchFile: 11: /user/username/projects/myproject/d.ts +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/e.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "CustomHandler::createFileWatcher", + "body": { + "id": 12, + "path": "/user/username/projects/myproject/e.ts" + } + } +Custom watchFile: 12: /user/username/projects/myproject/e.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 projectStateVersion: 6 projectProgramVersion: 3 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 (8) + /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/node_modules/something/index.d.ts Text-2 "export const x = 10;export const y = 20;" + /user/username/projects/myproject/m.ts Text-1 "import { x } from \"something\"" + /user/username/projects/myproject/c.ts Text-3 "export class a { prop = \"hello\"; foo() { return this.prop; } }export const y = 20;export const z = 30;" + /user/username/projects/myproject/d.ts Text-1 "export class a { prop = \"hello\"; foo() { return this.prop; } }" + /user/username/projects/myproject/e.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 '**/*' + node_modules/something/index.d.ts + Imported via "something" from file 'm.ts' + m.ts + Matched by default include pattern '**/*' + c.ts + Matched by default include pattern '**/*' + d.ts + Matched by default include pattern '**/*' + e.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 (8) + +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 (8) + +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":6,"path":"/a/lib/lib.d.ts"} +/user/username/projects/myproject/b.ts: + {"id":10,"path":"/user/username/projects/myproject/b.ts"} +/user/username/projects/myproject/c.ts: + {"id":9,"path":"/user/username/projects/myproject/c.ts"} +/user/username/projects/myproject/d.ts: *new* + {"id":11,"path":"/user/username/projects/myproject/d.ts"} +/user/username/projects/myproject/e.ts: *new* + {"id":12,"path":"/user/username/projects/myproject/e.ts"} +/user/username/projects/myproject/m.ts: + {"id":4,"path":"/user/username/projects/myproject/m.ts"} +/user/username/projects/myproject/tsconfig.json: + {"id":1,"path":"/user/username/projects/myproject/tsconfig.json"} + +Custom WatchedDirectoriesRecursive:: +/user/username/projects/myproject: + {"id":2,"path":"/user/username/projects/myproject","recursive":true,"ignoreUpdate":true} +/user/username/projects/myproject/node_modules: + {"id":5,"path":"/user/username/projects/myproject/node_modules","recursive":true} +/user/username/projects/myproject/node_modules/@types: + {"id":7,"path":"/user/username/projects/myproject/node_modules/@types","recursive":true,"ignoreUpdate":true} +/user/username/projects/node_modules/@types: + {"id":8,"path":"/user/username/projects/node_modules/@types","recursive":true,"ignoreUpdate":true} + +Projects:: +/user/username/projects/myproject/tsconfig.json (Configured) *changed* + projectStateVersion: 6 + projectProgramVersion: 4 *changed* + dirty: false *changed* + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/a.ts (Open) + version: SVC-1-0 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json *default* +/user/username/projects/myproject/b.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/c.ts *changed* + version: Text-3 *changed* + pendingReloadFromDisk: false *changed* + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/d.ts *new* + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/e.ts *new* + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/m.ts + version: Text-1 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json +/user/username/projects/myproject/node_modules/something/index.d.ts + version: Text-2 + containingProjects: 1 + /user/username/projects/myproject/tsconfig.json From 7b998fa0305f42e9f6a6aa3fc523004b3b0dbbf4 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 27 Mar 2024 11:40:26 -0700 Subject: [PATCH 4/4] API baseline --- tests/baselines/reference/api/typescript.d.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3c7a59ea0da74..091a0be278e63 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1342,12 +1342,13 @@ declare namespace ts { } export interface WatchChangeRequest extends Request { command: CommandTypes.WatchChange; - arguments: WatchChangeRequestArgs; + arguments: WatchChangeRequestArgs | readonly WatchChangeRequestArgs[]; } export interface WatchChangeRequestArgs { id: number; - path: string; - eventType: "create" | "delete" | "update"; + created?: string[]; + deleted?: string[]; + updated?: string[]; } /** * Request to obtain the list of files that should be regenerated if target file is recompiled. @@ -2032,6 +2033,7 @@ declare namespace ts { readonly id: number; readonly path: string; readonly recursive: boolean; + readonly ignoreUpdate?: boolean; } export type CloseFileWatcherEventName = "closeFileWatcher"; export interface CloseFileWatcherEvent extends Event {