Skip to content

Commit 2872a2d

Browse files
committed
Add model editor view tracker
This is used for registering which model editor views are currently active. This will be used to determine which view to send the "reveal method" command to. It can also be used in the future to limit the number of instances of the model editor that can be opened for a database. This uses the same pattern as variant analyses with a separate interface for the view to avoid having circular dependencies.
1 parent 8b8bacb commit 2872a2d

File tree

5 files changed

+73
-1
lines changed

5 files changed

+73
-1
lines changed

extensions/ql-vscode/src/model-editor/model-editor-module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import { setUpPack } from "./model-editor-queries";
2020
import { MethodModelingPanel } from "./method-modeling/method-modeling-panel";
2121
import { ModelingStore } from "./modeling-store";
2222
import { showResolvableLocation } from "../databases/local-databases/locations";
23+
import { ModelEditorViewTracker } from "./model-editor-view-tracker";
2324

2425
const SUPPORTED_LANGUAGES: string[] = ["java", "csharp"];
2526

2627
export class ModelEditorModule extends DisposableObject {
2728
private readonly queryStorageDir: string;
2829
private readonly modelingStore: ModelingStore;
30+
private readonly editorViewTracker: ModelEditorViewTracker<ModelEditorView>;
2931
private readonly methodsUsagePanel: MethodsUsagePanel;
3032
private readonly methodModelingPanel: MethodModelingPanel;
3133

@@ -39,6 +41,7 @@ export class ModelEditorModule extends DisposableObject {
3941
super();
4042
this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results");
4143
this.modelingStore = new ModelingStore(app);
44+
this.editorViewTracker = new ModelEditorViewTracker();
4245
this.methodsUsagePanel = this.push(
4346
new MethodsUsagePanel(this.modelingStore, cliServer),
4447
);
@@ -148,6 +151,7 @@ export class ModelEditorModule extends DisposableObject {
148151
const view = new ModelEditorView(
149152
this.app,
150153
this.modelingStore,
154+
this.editorViewTracker,
151155
this.databaseManager,
152156
this.cliServer,
153157
this.queryRunner,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
interface ModelEditorViewInterface {
2+
databaseUri: string;
3+
}
4+
5+
export class ModelEditorViewTracker<
6+
T extends ModelEditorViewInterface = ModelEditorViewInterface,
7+
> {
8+
private readonly views = new Map<string, T[]>();
9+
10+
constructor() {}
11+
12+
public registerView(view: T): void {
13+
const databaseUri = view.databaseUri;
14+
15+
if (!this.views.has(databaseUri)) {
16+
this.views.set(databaseUri, []);
17+
}
18+
19+
this.views.get(databaseUri)?.push(view);
20+
}
21+
22+
public unregisterView(view: T): void {
23+
const views = this.views.get(view.databaseUri);
24+
if (!views) {
25+
return;
26+
}
27+
28+
const index = views.indexOf(view);
29+
if (index !== -1) {
30+
views.splice(index, 1);
31+
}
32+
}
33+
34+
public getViews(databaseUri: string): T[] {
35+
return this.views.get(databaseUri) ?? [];
36+
}
37+
}

extensions/ql-vscode/src/model-editor/model-editor-view.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { getLanguageDisplayName } from "../common/query-language";
4242
import { AutoModeler } from "./auto-modeler";
4343
import { telemetryListener } from "../common/vscode/telemetry";
4444
import { ModelingStore } from "./modeling-store";
45+
import { ModelEditorViewTracker } from "./model-editor-view-tracker";
4546

4647
export class ModelEditorView extends AbstractWebview<
4748
ToModelEditorMessage,
@@ -52,6 +53,7 @@ export class ModelEditorView extends AbstractWebview<
5253
public constructor(
5354
protected readonly app: App,
5455
private readonly modelingStore: ModelingStore,
56+
private readonly viewTracker: ModelEditorViewTracker<ModelEditorView>,
5557
private readonly databaseManager: DatabaseManager,
5658
private readonly cliServer: CodeQLCliServer,
5759
private readonly queryRunner: QueryRunner,
@@ -66,6 +68,8 @@ export class ModelEditorView extends AbstractWebview<
6668
this.modelingStore.initializeStateForDb(databaseItem);
6769
this.registerToModelingStoreEvents();
6870

71+
this.viewTracker.registerView(this);
72+
6973
this.autoModeler = new AutoModeler(
7074
app,
7175
cliServer,
@@ -181,7 +185,7 @@ export class ModelEditorView extends AbstractWebview<
181185
}
182186

183187
protected onPanelDispose(): void {
184-
// Nothing to do here
188+
this.viewTracker.unregisterView(this);
185189
}
186190

187191
protected async onMessage(msg: FromModelEditorMessage): Promise<void> {
@@ -338,6 +342,10 @@ export class ModelEditorView extends AbstractWebview<
338342
]);
339343
}
340344

345+
public get databaseUri(): string {
346+
return this.databaseItem.databaseUri.toString();
347+
}
348+
341349
private async setViewState(): Promise<void> {
342350
const showLlmButton =
343351
this.databaseItem.language === "java" && showLlmGeneration();
@@ -497,6 +505,7 @@ export class ModelEditorView extends AbstractWebview<
497505
const view = new ModelEditorView(
498506
this.app,
499507
this.modelingStore,
508+
this.viewTracker,
500509
this.databaseManager,
501510
this.cliServer,
502511
this.queryRunner,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { mockedObject } from "../../vscode-tests/utils/mocking.helpers";
2+
import { ModelEditorViewTracker } from "../../../src/model-editor/model-editor-view-tracker";
3+
import { ModelEditorView } from "../../../src/model-editor/model-editor-view";
4+
5+
export function createMockModelEditorViewTracker({
6+
registerView = jest.fn(),
7+
unregisterView = jest.fn(),
8+
getViews = jest.fn(),
9+
}: {
10+
registerView?: ModelEditorViewTracker["registerView"];
11+
unregisterView?: ModelEditorViewTracker["unregisterView"];
12+
getViews?: ModelEditorViewTracker["getViews"];
13+
} = {}): ModelEditorViewTracker<ModelEditorView> {
14+
return mockedObject<ModelEditorViewTracker<ModelEditorView>>({
15+
registerView,
16+
unregisterView,
17+
getViews,
18+
});
19+
}

extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import { mockEmptyDatabaseManager } from "../query-testing/test-runner-helpers";
99
import { QueryRunner } from "../../../../src/query-server";
1010
import { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack";
1111
import { createMockModelingStore } from "../../../__mocks__/model-editor/modelingStoreMock";
12+
import { createMockModelEditorViewTracker } from "../../../__mocks__/model-editor/modelEditorViewTrackerMock";
1213

1314
describe("ModelEditorView", () => {
1415
const app = createMockApp({});
1516
const modelingStore = createMockModelingStore();
17+
const viewTracker = createMockModelEditorViewTracker();
1618
const databaseManager = mockEmptyDatabaseManager();
1719
const cliServer = mockedObject<CodeQLCliServer>({});
1820
const queryRunner = mockedObject<QueryRunner>({});
@@ -38,6 +40,7 @@ describe("ModelEditorView", () => {
3840
view = new ModelEditorView(
3941
app,
4042
modelingStore,
43+
viewTracker,
4144
databaseManager,
4245
cliServer,
4346
queryRunner,

0 commit comments

Comments
 (0)