microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ac22b7ee6ec9d38efb5324d7543b832c55b56f2c

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

npm/src/events.ts

163lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4import { ShotResult, Dump, Result } from "./common.js";
5import { log } from "./log.js";
6
7// Create strongly typed compiler events
8type QscEvent<T> = Event & { detail: T };
9interface QscEventMap {
10 Message: QscEvent<string>;
11 DumpMachine: QscEvent<Dump>;
12 Result: QscEvent<Result>;
13 uiResultsRefresh: QscEvent<void>;
14}
15
16// Union of valid message names
17type QscMessageNames = keyof QscEventMap;
18
19// Given the message name, return the type of the 'details' property
20type QscEventDetail<K extends QscMessageNames> = QscEventMap[K]["detail"];
21
22// Union of all possible events types that may be dispatched
23export type QscEvents = QscEventMap[keyof QscEventMap];
24
25type QscEventHandler<T extends Event> = (event: T) => void;
26
27// Strongly typed event target for compiler operations.
28export interface IQscEventTarget {
29 addEventListener<K extends keyof QscEventMap>(
30 type: K,
31 listener: QscEventHandler<QscEventMap[K]>
32 ): void;
33
34 removeEventListener<K extends keyof QscEventMap>(
35 type: K,
36 listener: QscEventHandler<QscEventMap[K]>
37 ): void;
38
39 dispatchEvent(event: QscEvents): boolean;
40}
41
42// Convenience method that also provides type safety
43export function makeEvent<K extends QscMessageNames>(
44 type: K,
45 detail: QscEventDetail<K>
46): QscEventMap[K] {
47 const event = new Event(type) as QscEventMap[K];
48 event.detail = detail;
49 return event;
50}
51
52function makeResultObj(): ShotResult {
53 return { success: false, result: "", events: [] };
54}
55
56export class QscEventTarget extends EventTarget implements IQscEventTarget {
57 private results: ShotResult[] = [];
58 private shotActive = false;
59 private animationFrameId = 0;
60 private supportsUiRefresh = false;
61
62 // Overrides for the base EventTarget methods to limit to expected event types
63 addEventListener<K extends keyof QscEventMap>(
64 type: K,
65 listener: QscEventHandler<QscEventMap[K]>
66 ): void {
67 super.addEventListener(type, listener as EventListener);
68 }
69
70 removeEventListener<K extends keyof QscEventMap>(
71 type: K,
72 listener: QscEventHandler<QscEventMap[K]>
73 ): void {
74 super.removeEventListener(type, listener as EventListener);
75 }
76
77 dispatchEvent(event: QscEvents): boolean {
78 if (log.getLogLevel() >= 4) log.debug("Dispatching event: %o", event);
79 return super.dispatchEvent(event);
80 }
81
82 /**
83 * @param captureEvents Set to true if this instance should record events internally
84 */
85 constructor(captureEvents: boolean) {
86 super();
87 this.supportsUiRefresh =
88 typeof globalThis.requestAnimationFrame === "function";
89
90 if (captureEvents) {
91 this.addEventListener("Message", (ev) => this.onMessage(ev.detail));
92 this.addEventListener("DumpMachine", (ev) =>
93 this.onDumpMachine(ev.detail)
94 );
95 this.addEventListener("Result", (ev) => this.onResult(ev.detail));
96 }
97 }
98
99 private onMessage(msg: string) {
100 this.ensureActiveShot();
101
102 const shotIdx = this.results.length - 1;
103 this.results[shotIdx].events.push({ type: "Message", message: msg });
104
105 this.queueUiRefresh();
106 }
107
108 private onDumpMachine(dump: Dump) {
109 this.ensureActiveShot();
110
111 const shotIdx = this.results.length - 1;
112 this.results[shotIdx].events.push({ type: "DumpMachine", state: dump });
113
114 this.queueUiRefresh();
115 }
116
117 private onResult(result: Result) {
118 this.ensureActiveShot();
119
120 const shotIdx = this.results.length - 1;
121
122 this.results[shotIdx].success = result.success;
123 this.results[shotIdx].result = result.value;
124 this.shotActive = false;
125
126 this.queueUiRefresh();
127 }
128
129 private ensureActiveShot() {
130 if (!this.shotActive) {
131 this.results.push(makeResultObj());
132 this.shotActive = true;
133 }
134 }
135
136 private queueUiRefresh() {
137 if (this.supportsUiRefresh && !this.animationFrameId) {
138 this.animationFrameId = requestAnimationFrame(() => {
139 this.onUiRefresh();
140 });
141 }
142 }
143
144 private onUiRefresh() {
145 this.animationFrameId = 0;
146 const uiRefreshEvent = makeEvent("uiResultsRefresh", undefined);
147 this.dispatchEvent(uiRefreshEvent);
148 }
149
150 getResults(): ShotResult[] {
151 return this.results;
152 }
153
154 resultCount(): number {
155 // May be one less than length if the last is still in flight
156 return this.shotActive ? this.results.length - 1 : this.results.length;
157 }
158
159 clearResults(): void {
160 this.results = [];
161 this.shotActive = false;
162 }
163}
164