Skip to content

Commit

Permalink
Merge pull request #3802 from asgerf/asgerf/use-message-from-extension
Browse files Browse the repository at this point in the history
Add useMessageFromExtension hook
  • Loading branch information
asgerf authored Nov 18, 2024
2 parents 873d4e3 + bd38355 commit ea45e38
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 309 deletions.
27 changes: 27 additions & 0 deletions extensions/ql-vscode/src/view/common/useMessageFromExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useEffect } from "react";

/**
* Invokes the given callback when a message is received from the extension.
*/
export function useMessageFromExtension<T>(
onEvent: (event: T) => void,
onEventDependencies: unknown[],
): void {
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
onEvent(evt.data as T);
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);

return () => {
window.removeEventListener("message", listener);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, onEventDependencies);
}
179 changes: 83 additions & 96 deletions extensions/ql-vscode/src/view/compare/Compare.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from "react";
import { useState, useRef } from "react";
import { styled } from "styled-components";

import type {
Expand All @@ -16,6 +16,7 @@ import CompareTable from "./CompareTable";

import "../results/resultsView.css";
import { assertNever } from "../../common/helpers-pure";
import { useMessageFromExtension } from "../common/useMessageFromExtension";

const Header = styled.div`
display: flex;
Expand Down Expand Up @@ -50,115 +51,101 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
comparison?.result &&
(comparison.result.to.length || comparison.result.from.length);

useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToCompareViewMessage = evt.data;
switch (msg.t) {
case "setComparisonQueryInfo":
setQueryInfo(msg);
break;
case "setComparisons":
setComparison(msg);
break;
case "streamingComparisonSetup":
setComparison(null);
streamingComparisonRef.current = msg;
break;
case "streamingComparisonAddResults": {
const prev = streamingComparisonRef.current;
if (prev === null) {
console.warn(
'Received "streamingComparisonAddResults" before "streamingComparisonSetup"',
);
break;
}
useMessageFromExtension<ToCompareViewMessage>((msg) => {
switch (msg.t) {
case "setComparisonQueryInfo":
setQueryInfo(msg);
break;
case "setComparisons":
setComparison(msg);
break;
case "streamingComparisonSetup":
setComparison(null);
streamingComparisonRef.current = msg;
break;
case "streamingComparisonAddResults": {
const prev = streamingComparisonRef.current;
if (prev === null) {
console.warn(
'Received "streamingComparisonAddResults" before "streamingComparisonSetup"',
);
break;
}

if (prev.id !== msg.id) {
console.warn(
'Received "streamingComparisonAddResults" with different id, ignoring',
);
break;
}
if (prev.id !== msg.id) {
console.warn(
'Received "streamingComparisonAddResults" with different id, ignoring',
);
break;
}

let result: QueryCompareResult;
switch (prev.result.kind) {
case "raw":
if (msg.result.kind !== "raw") {
throw new Error(
"Streaming comparison: expected raw results, got interpreted results",
);
}

result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
case "interpreted":
if (msg.result.kind !== "interpreted") {
throw new Error(
"Streaming comparison: expected interpreted results, got raw results",
);
}

result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
default:
throw new Error("Unexpected comparison result kind");
let result: QueryCompareResult;
switch (prev.result.kind) {
case "raw":
if (msg.result.kind !== "raw") {
throw new Error(
"Streaming comparison: expected raw results, got interpreted results",
);
}

streamingComparisonRef.current = {
...prev,
result,
result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};

break;
}
case "streamingComparisonComplete":
if (streamingComparisonRef.current === null) {
console.warn(
'Received "streamingComparisonComplete" before "streamingComparisonSetup"',
case "interpreted":
if (msg.result.kind !== "interpreted") {
throw new Error(
"Streaming comparison: expected interpreted results, got raw results",
);
setComparison(null);
break;
}

if (streamingComparisonRef.current.id !== msg.id) {
console.warn(
'Received "streamingComparisonComplete" with different id, ignoring',
);
break;
}

setComparison({
...streamingComparisonRef.current,
t: "setComparisons",
});
streamingComparisonRef.current = null;
break;
case "setUserSettings":
setUserSettings(msg.userSettings);
result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
default:
assertNever(msg);
throw new Error("Unexpected comparison result kind");
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);

streamingComparisonRef.current = {
...prev,
result,
};

break;
}
};
window.addEventListener("message", listener);
case "streamingComparisonComplete":
if (streamingComparisonRef.current === null) {
console.warn(
'Received "streamingComparisonComplete" before "streamingComparisonSetup"',
);
setComparison(null);
break;
}

if (streamingComparisonRef.current.id !== msg.id) {
console.warn(
'Received "streamingComparisonComplete" with different id, ignoring',
);
break;
}

return () => {
window.removeEventListener("message", listener);
};
setComparison({
...streamingComparisonRef.current,
t: "setComparisons",
});
streamingComparisonRef.current = null;
break;
case "setUserSettings":
setUserSettings(msg.userSettings);
break;
default:
assertNever(msg);
}
}, []);

if (!queryInfo || !comparison) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import type { ToDataFlowPathsMessage } from "../../common/interface-types";
import type { DataFlowPaths as DataFlowPathsDomainModel } from "../../variant-analysis/shared/data-flow-paths";
import { DataFlowPaths } from "./DataFlowPaths";
import { useMessageFromExtension } from "../common/useMessageFromExtension";

export type DataFlowPathsViewProps = {
dataFlowPaths?: DataFlowPathsDomainModel;
Expand All @@ -14,28 +15,12 @@ export function DataFlowPathsView({
DataFlowPathsDomainModel | undefined
>(initialDataFlowPaths);

useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToDataFlowPathsMessage = evt.data;
if (msg.t === "setDataFlowPaths") {
setDataFlowPaths(msg.dataFlowPaths);
useMessageFromExtension<ToDataFlowPathsMessage>((msg) => {
setDataFlowPaths(msg.dataFlowPaths);

// Scroll to the top of the page when we're rendering
// new data flow paths.
window.scrollTo(0, 0);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);

return () => {
window.removeEventListener("message", listener);
};
// Scroll to the top of the page when we're rendering
// new data flow paths.
window.scrollTo(0, 0);
}, []);

if (!dataFlowPaths) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { MethodModeling } from "./MethodModeling";
import { getModelingStatus } from "../../model-editor/shared/modeling-status";
import type { Method } from "../../model-editor/method";
Expand All @@ -12,6 +12,7 @@ import { NoMethodSelected } from "./NoMethodSelected";
import type { MethodModelingPanelViewState } from "../../model-editor/shared/view-state";
import { MethodAlreadyModeled } from "./MethodAlreadyModeled";
import { defaultModelConfig } from "../../model-editor/languages";
import { useMessageFromExtension } from "../common/useMessageFromExtension";

type Props = {
initialViewState?: MethodModelingPanelViewState;
Expand All @@ -36,47 +37,33 @@ export function MethodModelingView({
[modeledMethods, isMethodModified],
);

useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToMethodModelingMessage = evt.data;
switch (msg.t) {
case "setMethodModelingPanelViewState":
setViewState(msg.viewState);
break;
case "setInModelingMode":
setInModelingMode(msg.inModelingMode);
break;
case "setMultipleModeledMethods":
setModeledMethods(msg.modeledMethods);
break;
case "setMethodModified":
setIsMethodModified(msg.isModified);
break;
case "setNoMethodSelected":
setMethod(undefined);
setModeledMethods([]);
setIsMethodModified(false);
break;
case "setSelectedMethod":
setMethod(msg.method);
setModeledMethods(msg.modeledMethods);
setIsMethodModified(msg.isModified);
break;
default:
assertNever(msg);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);

return () => {
window.removeEventListener("message", listener);
};
useMessageFromExtension<ToMethodModelingMessage>((msg) => {
switch (msg.t) {
case "setMethodModelingPanelViewState":
setViewState(msg.viewState);
break;
case "setInModelingMode":
setInModelingMode(msg.inModelingMode);
break;
case "setMultipleModeledMethods":
setModeledMethods(msg.modeledMethods);
break;
case "setMethodModified":
setIsMethodModified(msg.isModified);
break;
case "setNoMethodSelected":
setMethod(undefined);
setModeledMethods([]);
setIsMethodModified(false);
break;
case "setSelectedMethod":
setMethod(msg.method);
setModeledMethods(msg.modeledMethods);
setIsMethodModified(msg.isModified);
break;
default:
assertNever(msg);
}
}, []);

if (!inModelingMode || !viewState?.language) {
Expand Down
Loading

0 comments on commit ea45e38

Please sign in to comment.