Reveal method in editor view
This will reveal a method for which "Review in editor" is clicked in the model editor view: it will expand the group (library/package) in which the method is located, scroll to the method, and highlight the method. If the user clicks anywhere on the page, the highlight will be removed, but the group will remain expanded.
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
|
||||
|
||||
@@ -347,7 +347,12 @@ export class ModelEditorView extends AbstractWebview<
|
||||
}
|
||||
|
||||
public async revealMethod(method: Method): Promise<void> {
|
||||
void this.app.logger.log(`Revealing method ${JSON.stringify(method)}`);
|
||||
this.panel?.reveal();
|
||||
|
||||
await this.postMessage({
|
||||
t: "revealMethod",
|
||||
method,
|
||||
});
|
||||
}
|
||||
|
||||
private async setViewState(): Promise<void> {
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user