Merge pull request #3439 from github/robertbrignull/modeling-mode
Refactor events to fix the method modeling panel getting stuck showing "start modeling"
This commit is contained in:
@@ -698,16 +698,15 @@ interface SetMethodModelingPanelViewStateMessage {
|
|||||||
viewState: MethodModelingPanelViewState;
|
viewState: MethodModelingPanelViewState;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetMethodMessage {
|
|
||||||
t: "setMethod";
|
|
||||||
method: Method | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SetMethodModifiedMessage {
|
interface SetMethodModifiedMessage {
|
||||||
t: "setMethodModified";
|
t: "setMethodModified";
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SetNoMethodSelectedMessage {
|
||||||
|
t: "setNoMethodSelected";
|
||||||
|
}
|
||||||
|
|
||||||
interface SetSelectedMethodMessage {
|
interface SetSelectedMethodMessage {
|
||||||
t: "setSelectedMethod";
|
t: "setSelectedMethod";
|
||||||
method: Method;
|
method: Method;
|
||||||
@@ -719,9 +718,9 @@ interface SetSelectedMethodMessage {
|
|||||||
|
|
||||||
export type ToMethodModelingMessage =
|
export type ToMethodModelingMessage =
|
||||||
| SetMethodModelingPanelViewStateMessage
|
| SetMethodModelingPanelViewStateMessage
|
||||||
| SetMethodMessage
|
|
||||||
| SetMultipleModeledMethodsMessage
|
| SetMultipleModeledMethodsMessage
|
||||||
| SetMethodModifiedMessage
|
| SetMethodModifiedMessage
|
||||||
|
| SetNoMethodSelectedMessage
|
||||||
| SetSelectedMethodMessage
|
| SetSelectedMethodMessage
|
||||||
| SetInModelingModeMessage
|
| SetInModelingModeMessage
|
||||||
| SetInProgressMessage
|
| SetInProgressMessage
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import { window } from "vscode";
|
|||||||
import type { App } from "../../common/app";
|
import type { App } from "../../common/app";
|
||||||
import { DisposableObject } from "../../common/disposable-object";
|
import { DisposableObject } from "../../common/disposable-object";
|
||||||
import { MethodModelingViewProvider } from "./method-modeling-view-provider";
|
import { MethodModelingViewProvider } from "./method-modeling-view-provider";
|
||||||
import type { Method } from "../method";
|
|
||||||
import type { ModelingStore } from "../modeling-store";
|
import type { ModelingStore } from "../modeling-store";
|
||||||
import { ModelConfigListener } from "../../config";
|
import { ModelConfigListener } from "../../config";
|
||||||
import type { DatabaseItem } from "../../databases/local-databases";
|
|
||||||
import type { ModelingEvents } from "../modeling-events";
|
import type { ModelingEvents } from "../modeling-events";
|
||||||
|
|
||||||
export class MethodModelingPanel extends DisposableObject {
|
export class MethodModelingPanel extends DisposableObject {
|
||||||
@@ -36,11 +34,4 @@ export class MethodModelingPanel extends DisposableObject {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setMethod(
|
|
||||||
databaseItem: DatabaseItem,
|
|
||||||
method: Method,
|
|
||||||
): Promise<void> {
|
|
||||||
await this.provider.setMethod(databaseItem, method);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type { ModelingEvents } from "../modeling-events";
|
|||||||
import type { QueryLanguage } from "../../common/query-language";
|
import type { QueryLanguage } from "../../common/query-language";
|
||||||
import { tryGetQueryLanguage } from "../../common/query-language";
|
import { tryGetQueryLanguage } from "../../common/query-language";
|
||||||
import { createModelConfig } from "../languages";
|
import { createModelConfig } from "../languages";
|
||||||
|
import type { ModeledMethod } from "../modeled-method";
|
||||||
|
|
||||||
export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
||||||
ToMethodModelingMessage,
|
ToMethodModelingMessage,
|
||||||
@@ -37,7 +38,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async onWebViewLoaded(): Promise<void> {
|
protected override async onWebViewLoaded(): Promise<void> {
|
||||||
await Promise.all([this.setViewState(), this.setInitialState()]);
|
await this.setInitialState();
|
||||||
this.registerToModelingEvents();
|
this.registerToModelingEvents();
|
||||||
this.registerToModelConfigEvents();
|
this.registerToModelConfigEvents();
|
||||||
}
|
}
|
||||||
@@ -52,46 +53,60 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setMethod(
|
private async setDatabaseItem(databaseItem: DatabaseItem): Promise<void> {
|
||||||
databaseItem: DatabaseItem | undefined,
|
this.databaseItem = databaseItem;
|
||||||
method: Method | undefined,
|
|
||||||
|
await this.postMessage({
|
||||||
|
t: "setInModelingMode",
|
||||||
|
inModelingMode: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.language = tryGetQueryLanguage(databaseItem.language);
|
||||||
|
await this.setViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setSelectedMethod(
|
||||||
|
databaseItem: DatabaseItem,
|
||||||
|
method: Method,
|
||||||
|
modeledMethods: readonly ModeledMethod[],
|
||||||
|
isModified: boolean,
|
||||||
|
isInProgress: boolean,
|
||||||
|
processedByAutoModel: boolean,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.databaseItem = databaseItem;
|
this.databaseItem = databaseItem;
|
||||||
this.language = databaseItem && tryGetQueryLanguage(databaseItem.language);
|
this.language = tryGetQueryLanguage(databaseItem.language);
|
||||||
|
|
||||||
if (this.isShowingView) {
|
await this.postMessage({
|
||||||
await this.postMessage({
|
t: "setSelectedMethod",
|
||||||
t: "setMethod",
|
method,
|
||||||
method,
|
modeledMethods,
|
||||||
});
|
isModified,
|
||||||
}
|
isInProgress,
|
||||||
|
processedByAutoModel,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setInitialState(): Promise<void> {
|
private async setInitialState(): Promise<void> {
|
||||||
if (this.modelingStore.hasStateForActiveDb()) {
|
await this.setViewState();
|
||||||
const selectedMethod = this.modelingStore.getSelectedMethodDetails();
|
|
||||||
if (selectedMethod) {
|
|
||||||
this.databaseItem = selectedMethod.databaseItem;
|
|
||||||
this.language = tryGetQueryLanguage(
|
|
||||||
selectedMethod.databaseItem.language,
|
|
||||||
);
|
|
||||||
this.method = selectedMethod.method;
|
|
||||||
|
|
||||||
await this.postMessage({
|
const stateForActiveDb = this.modelingStore.getStateForActiveDb();
|
||||||
t: "setSelectedMethod",
|
if (!stateForActiveDb) {
|
||||||
method: selectedMethod.method,
|
return;
|
||||||
modeledMethods: selectedMethod.modeledMethods,
|
}
|
||||||
isModified: selectedMethod.isModified,
|
|
||||||
isInProgress: selectedMethod.isInProgress,
|
|
||||||
processedByAutoModel: selectedMethod.processedByAutoModel,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.postMessage({
|
await this.setDatabaseItem(stateForActiveDb.databaseItem);
|
||||||
t: "setInModelingMode",
|
|
||||||
inModelingMode: true,
|
const selectedMethod = this.modelingStore.getSelectedMethodDetails();
|
||||||
});
|
if (selectedMethod) {
|
||||||
|
await this.setSelectedMethod(
|
||||||
|
stateForActiveDb.databaseItem,
|
||||||
|
selectedMethod.method,
|
||||||
|
selectedMethod.modeledMethods,
|
||||||
|
selectedMethod.isModified,
|
||||||
|
selectedMethod.isInProgress,
|
||||||
|
selectedMethod.processedByAutoModel,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,33 +198,21 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
this.push(
|
this.push(
|
||||||
this.modelingEvents.onSelectedMethodChanged(async (e) => {
|
this.modelingEvents.onSelectedMethodChanged(async (e) => {
|
||||||
if (this.webviewView) {
|
if (this.webviewView) {
|
||||||
this.method = e.method;
|
await this.setSelectedMethod(
|
||||||
this.databaseItem = e.databaseItem;
|
e.databaseItem,
|
||||||
this.language = tryGetQueryLanguage(e.databaseItem.language);
|
e.method,
|
||||||
|
e.modeledMethods,
|
||||||
await this.postMessage({
|
e.isModified,
|
||||||
t: "setSelectedMethod",
|
e.isInProgress,
|
||||||
method: e.method,
|
e.processedByAutoModel,
|
||||||
modeledMethods: e.modeledMethods,
|
);
|
||||||
isModified: e.isModified,
|
|
||||||
isInProgress: e.isInProgress,
|
|
||||||
processedByAutoModel: e.processedByAutoModel,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.push(
|
this.push(
|
||||||
this.modelingEvents.onDbOpened(async (databaseItem) => {
|
this.modelingEvents.onDbOpened(async (databaseItem) => {
|
||||||
this.databaseItem = databaseItem;
|
await this.setDatabaseItem(databaseItem);
|
||||||
|
|
||||||
await this.postMessage({
|
|
||||||
t: "setInModelingMode",
|
|
||||||
inModelingMode: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.language = tryGetQueryLanguage(databaseItem.language);
|
|
||||||
await this.setViewState();
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -223,7 +226,9 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dbUri === this.databaseItem?.databaseUri.toString()) {
|
if (dbUri === this.databaseItem?.databaseUri.toString()) {
|
||||||
await this.setMethod(undefined, undefined);
|
await this.postMessage({
|
||||||
|
t: "setNoMethodSelected",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class MethodsUsagePanel extends DisposableObject {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async revealItem(
|
private async revealItem(
|
||||||
methodSignature: string,
|
methodSignature: string,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -108,6 +108,12 @@ export class MethodsUsagePanel extends DisposableObject {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.push(
|
||||||
|
this.modelingEvents.onSelectedMethodChanged(async (event) => {
|
||||||
|
await this.revealItem(event.method.signature, event.usage);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleStateChangeEvent(): Promise<void> {
|
private async handleStateChangeEvent(): Promise<void> {
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
private readonly queryStorageDir: string;
|
private readonly queryStorageDir: string;
|
||||||
private readonly modelingStore: ModelingStore;
|
private readonly modelingStore: ModelingStore;
|
||||||
private readonly modelingEvents: ModelingEvents;
|
private readonly modelingEvents: ModelingEvents;
|
||||||
private readonly methodsUsagePanel: MethodsUsagePanel;
|
|
||||||
private readonly methodModelingPanel: MethodModelingPanel;
|
|
||||||
private readonly modelConfig: ModelConfigListener;
|
private readonly modelConfig: ModelConfigListener;
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
@@ -53,10 +51,10 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results");
|
this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results");
|
||||||
this.modelingEvents = new ModelingEvents(app);
|
this.modelingEvents = new ModelingEvents(app);
|
||||||
this.modelingStore = new ModelingStore(this.modelingEvents);
|
this.modelingStore = new ModelingStore(this.modelingEvents);
|
||||||
this.methodsUsagePanel = this.push(
|
this.push(
|
||||||
new MethodsUsagePanel(this.modelingStore, this.modelingEvents, cliServer),
|
new MethodsUsagePanel(this.modelingStore, this.modelingEvents, cliServer),
|
||||||
);
|
);
|
||||||
this.methodModelingPanel = this.push(
|
this.push(
|
||||||
new MethodModelingPanel(app, this.modelingStore, this.modelingEvents),
|
new MethodModelingPanel(app, this.modelingStore, this.modelingEvents),
|
||||||
);
|
);
|
||||||
this.modelConfig = this.push(new ModelConfigListener());
|
this.modelConfig = this.push(new ModelConfigListener());
|
||||||
@@ -107,7 +105,11 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
private registerToModelingEvents(): void {
|
private registerToModelingEvents(): void {
|
||||||
this.push(
|
this.push(
|
||||||
this.modelingEvents.onSelectedMethodChanged(async (event) => {
|
this.modelingEvents.onSelectedMethodChanged(async (event) => {
|
||||||
await this.showMethod(event.databaseItem, event.method, event.usage);
|
await showResolvableLocation(
|
||||||
|
event.usage.url,
|
||||||
|
event.databaseItem,
|
||||||
|
this.app.logger,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -126,16 +128,6 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async showMethod(
|
|
||||||
databaseItem: DatabaseItem,
|
|
||||||
method: Method,
|
|
||||||
usage: Usage,
|
|
||||||
): Promise<void> {
|
|
||||||
await this.methodsUsagePanel.revealItem(method.signature, usage);
|
|
||||||
await this.methodModelingPanel.setMethod(databaseItem, method);
|
|
||||||
await showResolvableLocation(usage.url, databaseItem, this.app.logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async openModelEditor(): Promise<void> {
|
private async openModelEditor(): Promise<void> {
|
||||||
{
|
{
|
||||||
const db = this.databaseManager.currentDatabaseItem;
|
const db = this.databaseManager.currentDatabaseItem;
|
||||||
|
|||||||
@@ -53,15 +53,19 @@ export function MethodModelingView({
|
|||||||
case "setInModelingMode":
|
case "setInModelingMode":
|
||||||
setInModelingMode(msg.inModelingMode);
|
setInModelingMode(msg.inModelingMode);
|
||||||
break;
|
break;
|
||||||
case "setMethod":
|
|
||||||
setMethod(msg.method);
|
|
||||||
break;
|
|
||||||
case "setMultipleModeledMethods":
|
case "setMultipleModeledMethods":
|
||||||
setModeledMethods(msg.modeledMethods);
|
setModeledMethods(msg.modeledMethods);
|
||||||
break;
|
break;
|
||||||
case "setMethodModified":
|
case "setMethodModified":
|
||||||
setIsMethodModified(msg.isModified);
|
setIsMethodModified(msg.isModified);
|
||||||
break;
|
break;
|
||||||
|
case "setNoMethodSelected":
|
||||||
|
setMethod(undefined);
|
||||||
|
setModeledMethods([]);
|
||||||
|
setIsMethodModified(false);
|
||||||
|
setIsModelingInProgress(false);
|
||||||
|
setIsProcessedByAutoModel(false);
|
||||||
|
break;
|
||||||
case "setSelectedMethod":
|
case "setSelectedMethod":
|
||||||
setMethod(msg.method);
|
setMethod(msg.method);
|
||||||
setModeledMethods(msg.modeledMethods);
|
setModeledMethods(msg.modeledMethods);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { ModelingEvents } from "../../../src/model-editor/modeling-events";
|
|||||||
export function createMockModelingEvents({
|
export function createMockModelingEvents({
|
||||||
onActiveDbChanged = jest.fn(),
|
onActiveDbChanged = jest.fn(),
|
||||||
onDbClosed = jest.fn(),
|
onDbClosed = jest.fn(),
|
||||||
|
onSelectedMethodChanged = jest.fn(),
|
||||||
onMethodsChanged = jest.fn(),
|
onMethodsChanged = jest.fn(),
|
||||||
onHideModeledMethodsChanged = jest.fn(),
|
onHideModeledMethodsChanged = jest.fn(),
|
||||||
onModeChanged = jest.fn(),
|
onModeChanged = jest.fn(),
|
||||||
@@ -16,6 +17,7 @@ export function createMockModelingEvents({
|
|||||||
}: {
|
}: {
|
||||||
onActiveDbChanged?: ModelingEvents["onActiveDbChanged"];
|
onActiveDbChanged?: ModelingEvents["onActiveDbChanged"];
|
||||||
onDbClosed?: ModelingEvents["onDbClosed"];
|
onDbClosed?: ModelingEvents["onDbClosed"];
|
||||||
|
onSelectedMethodChanged?: ModelingEvents["onSelectedMethodChanged"];
|
||||||
onMethodsChanged?: ModelingEvents["onMethodsChanged"];
|
onMethodsChanged?: ModelingEvents["onMethodsChanged"];
|
||||||
onHideModeledMethodsChanged?: ModelingEvents["onHideModeledMethodsChanged"];
|
onHideModeledMethodsChanged?: ModelingEvents["onHideModeledMethodsChanged"];
|
||||||
onModeChanged?: ModelingEvents["onModeChanged"];
|
onModeChanged?: ModelingEvents["onModeChanged"];
|
||||||
@@ -29,6 +31,7 @@ export function createMockModelingEvents({
|
|||||||
return mockedObject<ModelingEvents>({
|
return mockedObject<ModelingEvents>({
|
||||||
onActiveDbChanged,
|
onActiveDbChanged,
|
||||||
onDbClosed,
|
onDbClosed,
|
||||||
|
onSelectedMethodChanged,
|
||||||
onMethodsChanged,
|
onMethodsChanged,
|
||||||
onHideModeledMethodsChanged,
|
onHideModeledMethodsChanged,
|
||||||
onModeChanged,
|
onModeChanged,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { TreeView } from "vscode";
|
import type { TreeView } from "vscode";
|
||||||
import { window } from "vscode";
|
import { EventEmitter, window } from "vscode";
|
||||||
import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
|
import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
|
||||||
import type { Method } from "../../../../../src/model-editor/method";
|
import type { Method } from "../../../../../src/model-editor/method";
|
||||||
import { MethodsUsagePanel } from "../../../../../src/model-editor/methods-usage/methods-usage-panel";
|
import { MethodsUsagePanel } from "../../../../../src/model-editor/methods-usage/methods-usage-panel";
|
||||||
@@ -66,6 +66,8 @@ describe("MethodsUsagePanel", () => {
|
|||||||
const modeledMethods: Record<string, ModeledMethod[]> = {};
|
const modeledMethods: Record<string, ModeledMethod[]> = {};
|
||||||
const modifiedMethodSignatures: Set<string> = new Set();
|
const modifiedMethodSignatures: Set<string> = new Set();
|
||||||
const usage = createUsage();
|
const usage = createUsage();
|
||||||
|
const selectedMethodChangedEmitter: ModelingEvents["onSelectedMethodChangedEventEmitter"] =
|
||||||
|
new EventEmitter();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockTreeView = mockedObject<TreeView<unknown>>({
|
mockTreeView = mockedObject<TreeView<unknown>>({
|
||||||
@@ -74,7 +76,9 @@ describe("MethodsUsagePanel", () => {
|
|||||||
jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView);
|
jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView);
|
||||||
|
|
||||||
modelingStore = createMockModelingStore();
|
modelingStore = createMockModelingStore();
|
||||||
modelingEvents = createMockModelingEvents();
|
modelingEvents = createMockModelingEvents({
|
||||||
|
onSelectedMethodChanged: selectedMethodChangedEmitter.event,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reveal the correct item in the tree view", async () => {
|
it("should reveal the correct item in the tree view", async () => {
|
||||||
@@ -97,7 +101,15 @@ describe("MethodsUsagePanel", () => {
|
|||||||
modifiedMethodSignatures,
|
modifiedMethodSignatures,
|
||||||
);
|
);
|
||||||
|
|
||||||
await panel.revealItem(method.signature, usage);
|
selectedMethodChangedEmitter.fire({
|
||||||
|
databaseItem: dbItem,
|
||||||
|
method,
|
||||||
|
usage,
|
||||||
|
modeledMethods: modeledMethods[method.signature],
|
||||||
|
isModified: modifiedMethodSignatures.has(method.signature),
|
||||||
|
isInProgress: false,
|
||||||
|
processedByAutoModel: false,
|
||||||
|
});
|
||||||
|
|
||||||
expect(mockTreeView.reveal).toHaveBeenCalledWith(
|
expect(mockTreeView.reveal).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -124,7 +136,15 @@ describe("MethodsUsagePanel", () => {
|
|||||||
modifiedMethodSignatures,
|
modifiedMethodSignatures,
|
||||||
);
|
);
|
||||||
|
|
||||||
await panel.revealItem(method.signature, usage);
|
selectedMethodChangedEmitter.fire({
|
||||||
|
databaseItem: dbItem,
|
||||||
|
method,
|
||||||
|
usage,
|
||||||
|
modeledMethods: modeledMethods[method.signature],
|
||||||
|
isModified: modifiedMethodSignatures.has(method.signature),
|
||||||
|
isInProgress: false,
|
||||||
|
processedByAutoModel: false,
|
||||||
|
});
|
||||||
|
|
||||||
expect(mockTreeView.reveal).not.toHaveBeenCalled();
|
expect(mockTreeView.reveal).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user