Merge pull request #2896 from github/koesie10/reveal-in-editor
Add reveal in editor button to method modeling panel
This commit is contained in:
@@ -575,12 +575,18 @@ interface SetModeledMethodMessage {
|
||||
method: ModeledMethod;
|
||||
}
|
||||
|
||||
interface RevealMethodMessage {
|
||||
t: "revealMethod";
|
||||
method: Method;
|
||||
}
|
||||
|
||||
export type ToModelEditorMessage =
|
||||
| SetExtensionPackStateMessage
|
||||
| SetMethodsMessage
|
||||
| SetModeledMethodsMessage
|
||||
| SetModifiedMethodsMessage
|
||||
| SetInProgressMethodsMessage;
|
||||
| SetInProgressMethodsMessage
|
||||
| RevealMethodMessage;
|
||||
|
||||
export type FromModelEditorMessage =
|
||||
| ViewLoadedMsg
|
||||
@@ -597,9 +603,15 @@ export type FromModelEditorMessage =
|
||||
| HideModeledMethodsMessage
|
||||
| SetModeledMethodMessage;
|
||||
|
||||
interface RevealInEditorMessage {
|
||||
t: "revealInModelEditor";
|
||||
method: Method;
|
||||
}
|
||||
|
||||
export type FromMethodModelingMessage =
|
||||
| CommonFromViewMessages
|
||||
| SetModeledMethodMessage;
|
||||
| SetModeledMethodMessage
|
||||
| RevealInEditorMessage;
|
||||
|
||||
interface SetMethodMessage {
|
||||
t: "setMethod";
|
||||
|
||||
@@ -4,14 +4,23 @@ import { DisposableObject } from "../../common/disposable-object";
|
||||
import { MethodModelingViewProvider } from "./method-modeling-view-provider";
|
||||
import { Method } from "../method";
|
||||
import { ModelingStore } from "../modeling-store";
|
||||
import { ModelEditorViewTracker } from "../model-editor-view-tracker";
|
||||
|
||||
export class MethodModelingPanel extends DisposableObject {
|
||||
private readonly provider: MethodModelingViewProvider;
|
||||
|
||||
constructor(app: App, modelingStore: ModelingStore) {
|
||||
constructor(
|
||||
app: App,
|
||||
modelingStore: ModelingStore,
|
||||
editorViewTracker: ModelEditorViewTracker,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.provider = new MethodModelingViewProvider(app, modelingStore);
|
||||
this.provider = new MethodModelingViewProvider(
|
||||
app,
|
||||
modelingStore,
|
||||
editorViewTracker,
|
||||
);
|
||||
this.push(
|
||||
window.registerWebviewViewProvider(
|
||||
MethodModelingViewProvider.viewType,
|
||||
|
||||
@@ -8,8 +8,10 @@ import { extLogger } from "../../common/logging/vscode/loggers";
|
||||
import { App } from "../../common/app";
|
||||
import { redactableError } from "../../common/errors";
|
||||
import { Method } from "../method";
|
||||
import { ModelingStore } from "../modeling-store";
|
||||
import { DbModelingState, ModelingStore } from "../modeling-store";
|
||||
import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider";
|
||||
import { assertNever } from "../../common/helpers-pure";
|
||||
import { ModelEditorViewTracker } from "../model-editor-view-tracker";
|
||||
|
||||
export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
||||
ToMethodModelingMessage,
|
||||
@@ -22,6 +24,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
||||
constructor(
|
||||
app: App,
|
||||
private readonly modelingStore: ModelingStore,
|
||||
private readonly editorViewTracker: ModelEditorViewTracker,
|
||||
) {
|
||||
super(app, "method-modeling");
|
||||
}
|
||||
@@ -77,19 +80,45 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
||||
break;
|
||||
|
||||
case "setModeledMethod": {
|
||||
const activeState = this.modelingStore.getStateForActiveDb();
|
||||
if (!activeState) {
|
||||
throw new Error("No active state found in modeling store");
|
||||
}
|
||||
const activeState = this.ensureActiveState();
|
||||
|
||||
this.modelingStore.updateModeledMethod(
|
||||
activeState.databaseItem,
|
||||
msg.method,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "revealInModelEditor":
|
||||
await this.revealInModelEditor(msg.method);
|
||||
|
||||
break;
|
||||
default:
|
||||
assertNever(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private async revealInModelEditor(method: Method): Promise<void> {
|
||||
const activeState = this.ensureActiveState();
|
||||
|
||||
const views = this.editorViewTracker.getViews(
|
||||
activeState.databaseItem.databaseUri.toString(),
|
||||
);
|
||||
if (views.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(views.map((view) => view.revealMethod(method)));
|
||||
}
|
||||
|
||||
private ensureActiveState(): DbModelingState {
|
||||
const activeState = this.modelingStore.getStateForActiveDb();
|
||||
if (!activeState) {
|
||||
throw new Error("No active state found in modeling store");
|
||||
}
|
||||
|
||||
return activeState;
|
||||
}
|
||||
|
||||
private registerToModelingStoreEvents(): void {
|
||||
this.push(
|
||||
this.modelingStore.onModeledMethodsChanged(async (e) => {
|
||||
|
||||
@@ -20,12 +20,14 @@ import { setUpPack } from "./model-editor-queries";
|
||||
import { MethodModelingPanel } from "./method-modeling/method-modeling-panel";
|
||||
import { ModelingStore } from "./modeling-store";
|
||||
import { showResolvableLocation } from "../databases/local-databases/locations";
|
||||
import { ModelEditorViewTracker } from "./model-editor-view-tracker";
|
||||
|
||||
const SUPPORTED_LANGUAGES: string[] = ["java", "csharp"];
|
||||
|
||||
export class ModelEditorModule extends DisposableObject {
|
||||
private readonly queryStorageDir: string;
|
||||
private readonly modelingStore: ModelingStore;
|
||||
private readonly editorViewTracker: ModelEditorViewTracker<ModelEditorView>;
|
||||
private readonly methodsUsagePanel: MethodsUsagePanel;
|
||||
private readonly methodModelingPanel: MethodModelingPanel;
|
||||
|
||||
@@ -39,11 +41,12 @@ export class ModelEditorModule extends DisposableObject {
|
||||
super();
|
||||
this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results");
|
||||
this.modelingStore = new ModelingStore(app);
|
||||
this.editorViewTracker = new ModelEditorViewTracker();
|
||||
this.methodsUsagePanel = this.push(
|
||||
new MethodsUsagePanel(this.modelingStore, cliServer),
|
||||
);
|
||||
this.methodModelingPanel = this.push(
|
||||
new MethodModelingPanel(app, this.modelingStore),
|
||||
new MethodModelingPanel(app, this.modelingStore, this.editorViewTracker),
|
||||
);
|
||||
|
||||
this.registerToModelingStoreEvents();
|
||||
@@ -148,6 +151,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
const view = new ModelEditorView(
|
||||
this.app,
|
||||
this.modelingStore,
|
||||
this.editorViewTracker,
|
||||
this.databaseManager,
|
||||
this.cliServer,
|
||||
this.queryRunner,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Method } from "./method";
|
||||
|
||||
interface ModelEditorViewInterface {
|
||||
databaseUri: string;
|
||||
|
||||
revealMethod(method: Method): Promise<void>;
|
||||
}
|
||||
|
||||
export class ModelEditorViewTracker<
|
||||
T extends ModelEditorViewInterface = ModelEditorViewInterface,
|
||||
> {
|
||||
private readonly views = new Map<string, T[]>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
public registerView(view: T): void {
|
||||
const databaseUri = view.databaseUri;
|
||||
|
||||
if (!this.views.has(databaseUri)) {
|
||||
this.views.set(databaseUri, []);
|
||||
}
|
||||
|
||||
this.views.get(databaseUri)?.push(view);
|
||||
}
|
||||
|
||||
public unregisterView(view: T): void {
|
||||
const views = this.views.get(view.databaseUri);
|
||||
if (!views) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = views.indexOf(view);
|
||||
if (index !== -1) {
|
||||
views.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public getViews(databaseUri: string): T[] {
|
||||
return this.views.get(databaseUri) ?? [];
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import { getLanguageDisplayName } from "../common/query-language";
|
||||
import { AutoModeler } from "./auto-modeler";
|
||||
import { telemetryListener } from "../common/vscode/telemetry";
|
||||
import { ModelingStore } from "./modeling-store";
|
||||
import { ModelEditorViewTracker } from "./model-editor-view-tracker";
|
||||
|
||||
export class ModelEditorView extends AbstractWebview<
|
||||
ToModelEditorMessage,
|
||||
@@ -52,6 +53,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
public constructor(
|
||||
protected readonly app: App,
|
||||
private readonly modelingStore: ModelingStore,
|
||||
private readonly viewTracker: ModelEditorViewTracker<ModelEditorView>,
|
||||
private readonly databaseManager: DatabaseManager,
|
||||
private readonly cliServer: CodeQLCliServer,
|
||||
private readonly queryRunner: QueryRunner,
|
||||
@@ -66,6 +68,8 @@ export class ModelEditorView extends AbstractWebview<
|
||||
this.modelingStore.initializeStateForDb(databaseItem);
|
||||
this.registerToModelingStoreEvents();
|
||||
|
||||
this.viewTracker.registerView(this);
|
||||
|
||||
this.autoModeler = new AutoModeler(
|
||||
app,
|
||||
cliServer,
|
||||
@@ -181,7 +185,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
}
|
||||
|
||||
protected onPanelDispose(): void {
|
||||
// Nothing to do here
|
||||
this.viewTracker.unregisterView(this);
|
||||
}
|
||||
|
||||
protected async onMessage(msg: FromModelEditorMessage): Promise<void> {
|
||||
@@ -338,6 +342,19 @@ export class ModelEditorView extends AbstractWebview<
|
||||
]);
|
||||
}
|
||||
|
||||
public get databaseUri(): string {
|
||||
return this.databaseItem.databaseUri.toString();
|
||||
}
|
||||
|
||||
public async revealMethod(method: Method): Promise<void> {
|
||||
this.panel?.reveal();
|
||||
|
||||
await this.postMessage({
|
||||
t: "revealMethod",
|
||||
method,
|
||||
});
|
||||
}
|
||||
|
||||
private async setViewState(): Promise<void> {
|
||||
const showLlmButton =
|
||||
this.databaseItem.language === "java" && showLlmGeneration();
|
||||
@@ -497,6 +514,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
const view = new ModelEditorView(
|
||||
this.app,
|
||||
this.modelingStore,
|
||||
this.viewTracker,
|
||||
this.databaseManager,
|
||||
this.cliServer,
|
||||
this.queryRunner,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Method, Usage } from "./method";
|
||||
import { ModeledMethod } from "./modeled-method";
|
||||
import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "./shared/hide-modeled-methods";
|
||||
|
||||
interface DbModelingState {
|
||||
export interface DbModelingState {
|
||||
databaseItem: DatabaseItem;
|
||||
methods: Method[];
|
||||
hideModeledMethods: boolean;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { MethodName } from "../model-editor/MethodName";
|
||||
import { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import { MethodModelingInputs } from "./MethodModelingInputs";
|
||||
import { VSCodeTag } from "@vscode/webview-ui-toolkit/react";
|
||||
import { ReviewInEditorButton } from "./ReviewInEditorButton";
|
||||
|
||||
const Container = styled.div`
|
||||
padding: 0.3rem;
|
||||
@@ -64,6 +65,7 @@ export const MethodModeling = ({
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<ReviewInEditorButton method={method} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { styled } from "styled-components";
|
||||
import { vscode } from "../vscode-api";
|
||||
import TextButton from "../common/TextButton";
|
||||
import { Method } from "../../model-editor/method";
|
||||
|
||||
const Button = styled(TextButton)`
|
||||
margin-top: 0.5rem;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
method: Method;
|
||||
};
|
||||
|
||||
export const ReviewInEditorButton = ({ method }: Props) => {
|
||||
const handleClick = useCallback(() => {
|
||||
vscode.postMessage({
|
||||
t: "revealInModelEditor",
|
||||
method,
|
||||
});
|
||||
}, [method]);
|
||||
|
||||
return <Button onClick={handleClick}>Review in editor</Button>;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { styled } from "styled-components";
|
||||
import { Method } from "../../model-editor/method";
|
||||
import { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
@@ -76,6 +76,7 @@ export type LibraryRowProps = {
|
||||
inProgressMethods: InProgressMethods;
|
||||
viewState: ModelEditorViewState;
|
||||
hideModeledMethods: boolean;
|
||||
revealedMethodSignature: string | null;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
onSaveModelClick: (
|
||||
methods: Method[],
|
||||
@@ -100,6 +101,7 @@ export const LibraryRow = ({
|
||||
inProgressMethods,
|
||||
viewState,
|
||||
hideModeledMethods,
|
||||
revealedMethodSignature,
|
||||
onChange,
|
||||
onSaveModelClick,
|
||||
onGenerateFromLlmClick,
|
||||
@@ -117,6 +119,14 @@ export const LibraryRow = ({
|
||||
setExpanded((oldIsExpanded) => !oldIsExpanded);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// If any of the methods in this group is the one that should be revealed, we should expand
|
||||
// this group so the method can highlight itself.
|
||||
if (methods.some((m) => m.signature === revealedMethodSignature)) {
|
||||
setExpanded(true);
|
||||
}
|
||||
}, [methods, revealedMethodSignature]);
|
||||
|
||||
const handleModelWithAI = useCallback(
|
||||
async (e: React.MouseEvent) => {
|
||||
onGenerateFromLlmClick(title, methods, modeledMethods);
|
||||
@@ -227,6 +237,7 @@ export const LibraryRow = ({
|
||||
inProgressMethods={inProgressMethods}
|
||||
mode={viewState.mode}
|
||||
hideModeledMethods={hideModeledMethods}
|
||||
revealedMethodSignature={revealedMethodSignature}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<SectionDivider />
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
VSCodeProgressRing,
|
||||
} from "@vscode/webview-ui-toolkit/react";
|
||||
import * as React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { forwardRef, useCallback, useEffect, useRef } from "react";
|
||||
import { styled } from "styled-components";
|
||||
import { vscode } from "../vscode-api";
|
||||
|
||||
@@ -47,6 +47,11 @@ const ProgressRing = styled(VSCodeProgressRing)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
const DataGridRow = styled(VSCodeDataGridRow)<{ focused?: boolean }>`
|
||||
outline: ${(props) =>
|
||||
props.focused ? "1px solid var(--vscode-focusBorder)" : "none"};
|
||||
`;
|
||||
|
||||
export type MethodRowProps = {
|
||||
method: Method;
|
||||
methodCanBeModeled: boolean;
|
||||
@@ -54,97 +59,126 @@ export type MethodRowProps = {
|
||||
methodIsUnsaved: boolean;
|
||||
modelingInProgress: boolean;
|
||||
mode: Mode;
|
||||
revealedMethodSignature: string | null;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
export const MethodRow = (props: MethodRowProps) => {
|
||||
const { methodCanBeModeled } = props;
|
||||
const { method, methodCanBeModeled, revealedMethodSignature } = props;
|
||||
|
||||
const ref = useRef<HTMLElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (method.signature === revealedMethodSignature) {
|
||||
ref.current?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
}
|
||||
}, [method, revealedMethodSignature]);
|
||||
|
||||
if (methodCanBeModeled) {
|
||||
return <ModelableMethodRow {...props} />;
|
||||
return <ModelableMethodRow {...props} ref={ref} />;
|
||||
} else {
|
||||
return <UnmodelableMethodRow {...props} />;
|
||||
return <UnmodelableMethodRow {...props} ref={ref} />;
|
||||
}
|
||||
};
|
||||
|
||||
function ModelableMethodRow(props: MethodRowProps) {
|
||||
const { method, modeledMethod, methodIsUnsaved, mode, onChange } = props;
|
||||
const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
(props, ref) => {
|
||||
const {
|
||||
method,
|
||||
modeledMethod,
|
||||
methodIsUnsaved,
|
||||
mode,
|
||||
revealedMethodSignature,
|
||||
onChange,
|
||||
} = props;
|
||||
|
||||
const jumpToUsage = useCallback(
|
||||
() => sendJumpToUsageMessage(method),
|
||||
[method],
|
||||
);
|
||||
const jumpToUsage = useCallback(
|
||||
() => sendJumpToUsageMessage(method),
|
||||
[method],
|
||||
);
|
||||
|
||||
const modelingStatus = getModelingStatus(modeledMethod, methodIsUnsaved);
|
||||
const modelingStatus = getModelingStatus(modeledMethod, methodIsUnsaved);
|
||||
|
||||
return (
|
||||
<VSCodeDataGridRow data-testid="modelable-method-row">
|
||||
<ApiOrMethodCell gridColumn={1}>
|
||||
<ModelingStatusIndicator status={modelingStatus} />
|
||||
<MethodClassifications method={method} />
|
||||
<MethodName {...props.method} />
|
||||
{mode === Mode.Application && (
|
||||
<UsagesButton onClick={jumpToUsage}>
|
||||
{method.usages.length}
|
||||
</UsagesButton>
|
||||
return (
|
||||
<DataGridRow
|
||||
data-testid="modelable-method-row"
|
||||
ref={ref}
|
||||
focused={revealedMethodSignature === method.signature}
|
||||
>
|
||||
<ApiOrMethodCell gridColumn={1}>
|
||||
<ModelingStatusIndicator status={modelingStatus} />
|
||||
<MethodClassifications method={method} />
|
||||
<MethodName {...props.method} />
|
||||
{mode === Mode.Application && (
|
||||
<UsagesButton onClick={jumpToUsage}>
|
||||
{method.usages.length}
|
||||
</UsagesButton>
|
||||
)}
|
||||
<ViewLink onClick={jumpToUsage}>View</ViewLink>
|
||||
{props.modelingInProgress && <ProgressRing />}
|
||||
</ApiOrMethodCell>
|
||||
{props.modelingInProgress && (
|
||||
<>
|
||||
<VSCodeDataGridCell gridColumn={2}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={3}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={4}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={5}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
</>
|
||||
)}
|
||||
<ViewLink onClick={jumpToUsage}>View</ViewLink>
|
||||
{props.modelingInProgress && <ProgressRing />}
|
||||
</ApiOrMethodCell>
|
||||
{props.modelingInProgress && (
|
||||
<>
|
||||
<VSCodeDataGridCell gridColumn={2}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={3}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={4}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={5}>
|
||||
<InProgressDropdown />
|
||||
</VSCodeDataGridCell>
|
||||
</>
|
||||
)}
|
||||
{!props.modelingInProgress && (
|
||||
<>
|
||||
<VSCodeDataGridCell gridColumn={2}>
|
||||
<ModelTypeDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={3}>
|
||||
<ModelInputDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={4}>
|
||||
<ModelOutputDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={5}>
|
||||
<ModelKindDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
</>
|
||||
)}
|
||||
</VSCodeDataGridRow>
|
||||
);
|
||||
}
|
||||
{!props.modelingInProgress && (
|
||||
<>
|
||||
<VSCodeDataGridCell gridColumn={2}>
|
||||
<ModelTypeDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={3}>
|
||||
<ModelInputDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={4}>
|
||||
<ModelOutputDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={5}>
|
||||
<ModelKindDropdown
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
</>
|
||||
)}
|
||||
</DataGridRow>
|
||||
);
|
||||
},
|
||||
);
|
||||
ModelableMethodRow.displayName = "ModelableMethodRow";
|
||||
|
||||
function UnmodelableMethodRow(props: MethodRowProps) {
|
||||
const { method, mode } = props;
|
||||
const UnmodelableMethodRow = forwardRef<
|
||||
HTMLElement | undefined,
|
||||
MethodRowProps
|
||||
>((props, ref) => {
|
||||
const { method, mode, revealedMethodSignature } = props;
|
||||
|
||||
const jumpToUsage = useCallback(
|
||||
() => sendJumpToUsageMessage(method),
|
||||
@@ -152,7 +186,11 @@ function UnmodelableMethodRow(props: MethodRowProps) {
|
||||
);
|
||||
|
||||
return (
|
||||
<VSCodeDataGridRow data-testid="unmodelable-method-row">
|
||||
<DataGridRow
|
||||
data-testid="unmodelable-method-row"
|
||||
ref={ref}
|
||||
focused={revealedMethodSignature === method.signature}
|
||||
>
|
||||
<ApiOrMethodCell gridColumn={1}>
|
||||
<ModelingStatusIndicator status="saved" />
|
||||
<MethodName {...props.method} />
|
||||
@@ -167,9 +205,10 @@ function UnmodelableMethodRow(props: MethodRowProps) {
|
||||
<VSCodeDataGridCell gridColumn="span 4">
|
||||
Method already modeled
|
||||
</VSCodeDataGridCell>
|
||||
</VSCodeDataGridRow>
|
||||
</DataGridRow>
|
||||
);
|
||||
}
|
||||
});
|
||||
UnmodelableMethodRow.displayName = "UnmodelableMethodRow";
|
||||
|
||||
function sendJumpToUsageMessage(method: Method) {
|
||||
vscode.postMessage({
|
||||
|
||||
@@ -101,6 +101,10 @@ export function ModelEditor({
|
||||
initialHideModeledMethods,
|
||||
);
|
||||
|
||||
const [revealedMethodSignature, setRevealedMethodSignature] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
vscode.postMessage({
|
||||
t: "hideModeledMethods",
|
||||
@@ -136,6 +140,10 @@ export function ModelEditor({
|
||||
new Set(msg.inProgressMethods),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case "revealMethod":
|
||||
setRevealedMethodSignature(msg.method.signature);
|
||||
|
||||
break;
|
||||
default:
|
||||
assertNever(msg);
|
||||
@@ -153,6 +161,20 @@ export function ModelEditor({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// If there is a revealed method signature, hide it when the user clicks anywhere. In this case, we do need to
|
||||
// show the user where the method is anymore and they should have seen it.
|
||||
const listener = () => {
|
||||
setRevealedMethodSignature(null);
|
||||
};
|
||||
|
||||
window.addEventListener("click", listener);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("click", listener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const modeledPercentage = useMemo(
|
||||
() => calculateModeledPercentage(methods),
|
||||
[methods],
|
||||
@@ -323,6 +345,7 @@ export function ModelEditor({
|
||||
inProgressMethods={inProgressMethods}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={hideModeledMethods}
|
||||
revealedMethodSignature={revealedMethodSignature}
|
||||
onChange={onChange}
|
||||
onSaveModelClick={onSaveModelClick}
|
||||
onGenerateFromLlmClick={onGenerateFromLlmClick}
|
||||
|
||||
@@ -23,6 +23,7 @@ export type ModeledMethodDataGridProps = {
|
||||
inProgressMethods: InProgressMethods;
|
||||
mode: Mode;
|
||||
hideModeledMethods: boolean;
|
||||
revealedMethodSignature: string | null;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
@@ -34,6 +35,7 @@ export const ModeledMethodDataGrid = ({
|
||||
inProgressMethods,
|
||||
mode,
|
||||
hideModeledMethods,
|
||||
revealedMethodSignature,
|
||||
onChange,
|
||||
}: ModeledMethodDataGridProps) => {
|
||||
const [methodsWithModelability, numHiddenMethods]: [
|
||||
@@ -94,6 +96,7 @@ export const ModeledMethodDataGrid = ({
|
||||
method.signature,
|
||||
)}
|
||||
mode={mode}
|
||||
revealedMethodSignature={revealedMethodSignature}
|
||||
onChange={onChange}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -16,6 +16,7 @@ export type ModeledMethodsListProps = {
|
||||
modeledMethods: Record<string, ModeledMethod>;
|
||||
modifiedSignatures: Set<string>;
|
||||
inProgressMethods: InProgressMethods;
|
||||
revealedMethodSignature: string | null;
|
||||
viewState: ModelEditorViewState;
|
||||
hideModeledMethods: boolean;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
@@ -44,6 +45,7 @@ export const ModeledMethodsList = ({
|
||||
inProgressMethods,
|
||||
viewState,
|
||||
hideModeledMethods,
|
||||
revealedMethodSignature,
|
||||
onChange,
|
||||
onSaveModelClick,
|
||||
onGenerateFromLlmClick,
|
||||
@@ -89,6 +91,7 @@ export const ModeledMethodsList = ({
|
||||
inProgressMethods={inProgressMethods}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={hideModeledMethods}
|
||||
revealedMethodSignature={revealedMethodSignature}
|
||||
onChange={onChange}
|
||||
onSaveModelClick={onSaveModelClick}
|
||||
onGenerateFromLlmClick={onGenerateFromLlmClick}
|
||||
|
||||
@@ -43,6 +43,7 @@ describe(LibraryRow.name, () => {
|
||||
inProgressMethods={new InProgressMethods()}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={false}
|
||||
revealedMethodSignature={null}
|
||||
onChange={onChange}
|
||||
onSaveModelClick={onSaveModelClick}
|
||||
onGenerateFromLlmClick={onGenerateFromLlmClick}
|
||||
|
||||
@@ -39,6 +39,7 @@ describe(MethodRow.name, () => {
|
||||
modeledMethod={modeledMethod}
|
||||
methodIsUnsaved={false}
|
||||
modelingInProgress={false}
|
||||
revealedMethodSignature={null}
|
||||
mode={Mode.Application}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
|
||||
@@ -60,6 +60,7 @@ describe(ModeledMethodDataGrid.name, () => {
|
||||
inProgressMethods={new InProgressMethods()}
|
||||
mode={Mode.Application}
|
||||
hideModeledMethods={false}
|
||||
revealedMethodSignature={null}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
/>,
|
||||
|
||||
@@ -69,6 +69,7 @@ describe(ModeledMethodsList.name, () => {
|
||||
inProgressMethods={new InProgressMethods()}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={false}
|
||||
revealedMethodSignature={null}
|
||||
onChange={onChange}
|
||||
onSaveModelClick={onSaveModelClick}
|
||||
onGenerateFromLlmClick={onGenerateFromLlmClick}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
FromCompareViewMessage,
|
||||
FromMethodModelingMessage,
|
||||
FromModelEditorMessage,
|
||||
FromResultsViewMsg,
|
||||
FromVariantAnalysisMessage,
|
||||
@@ -15,7 +16,8 @@ export interface VsCodeApi {
|
||||
| FromResultsViewMsg
|
||||
| FromCompareViewMessage
|
||||
| FromVariantAnalysisMessage
|
||||
| FromModelEditorMessage,
|
||||
| FromModelEditorMessage
|
||||
| FromMethodModelingMessage,
|
||||
): void;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { mockedObject } from "../../vscode-tests/utils/mocking.helpers";
|
||||
import { ModelEditorViewTracker } from "../../../src/model-editor/model-editor-view-tracker";
|
||||
import { ModelEditorView } from "../../../src/model-editor/model-editor-view";
|
||||
|
||||
export function createMockModelEditorViewTracker({
|
||||
registerView = jest.fn(),
|
||||
unregisterView = jest.fn(),
|
||||
getViews = jest.fn(),
|
||||
}: {
|
||||
registerView?: ModelEditorViewTracker["registerView"];
|
||||
unregisterView?: ModelEditorViewTracker["unregisterView"];
|
||||
getViews?: ModelEditorViewTracker["getViews"];
|
||||
} = {}): ModelEditorViewTracker<ModelEditorView> {
|
||||
return mockedObject<ModelEditorViewTracker<ModelEditorView>>({
|
||||
registerView,
|
||||
unregisterView,
|
||||
getViews,
|
||||
});
|
||||
}
|
||||
@@ -9,10 +9,12 @@ import { mockEmptyDatabaseManager } from "../query-testing/test-runner-helpers";
|
||||
import { QueryRunner } from "../../../../src/query-server";
|
||||
import { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack";
|
||||
import { createMockModelingStore } from "../../../__mocks__/model-editor/modelingStoreMock";
|
||||
import { createMockModelEditorViewTracker } from "../../../__mocks__/model-editor/modelEditorViewTrackerMock";
|
||||
|
||||
describe("ModelEditorView", () => {
|
||||
const app = createMockApp({});
|
||||
const modelingStore = createMockModelingStore();
|
||||
const viewTracker = createMockModelEditorViewTracker();
|
||||
const databaseManager = mockEmptyDatabaseManager();
|
||||
const cliServer = mockedObject<CodeQLCliServer>({});
|
||||
const queryRunner = mockedObject<QueryRunner>({});
|
||||
@@ -38,6 +40,7 @@ describe("ModelEditorView", () => {
|
||||
view = new ModelEditorView(
|
||||
app,
|
||||
modelingStore,
|
||||
viewTracker,
|
||||
databaseManager,
|
||||
cliServer,
|
||||
queryRunner,
|
||||
|
||||
Reference in New Issue
Block a user