Skip to content

Commit 0e1afce

Browse files
committed
Sort methods in usages panel according to model editor sort order
This sorts the methods in the methods usages panel the same as in the model editor. Since this is dependent on the mode, we need to keep track of the mode in the modeling store, so this also adds a mode field to the db state.
1 parent 2b47d3d commit 0e1afce

File tree

9 files changed

+223
-15
lines changed

9 files changed

+223
-15
lines changed

extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,21 @@ import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "../shared/hide-modeled-metho
1717
import { getModelingStatus } from "../shared/modeling-status";
1818
import { assertNever } from "../../common/helpers-pure";
1919
import { ModeledMethod } from "../modeled-method";
20+
import { groupMethods, sortGroupNames, sortMethods } from "../shared/sorting";
21+
import { INITIAL_MODE, Mode } from "../shared/mode";
2022

2123
export class MethodsUsageDataProvider
2224
extends DisposableObject
2325
implements TreeDataProvider<MethodsUsageTreeViewItem>
2426
{
2527
private methods: Method[] = [];
28+
// sortedMethods is a separate field so we can check if the methods have changed
29+
// by reference, which is faster than checking if the methods have changed by value.
30+
private sortedMethods: Method[] = [];
2631
private databaseItem: DatabaseItem | undefined = undefined;
2732
private sourceLocationPrefix: string | undefined = undefined;
2833
private hideModeledMethods: boolean = INITIAL_HIDE_MODELED_METHODS_VALUE;
34+
private mode: Mode = INITIAL_MODE;
2935
private modeledMethods: Record<string, ModeledMethod[]> = {};
3036
private modifiedMethodSignatures: Set<string> = new Set();
3137

@@ -52,21 +58,25 @@ export class MethodsUsageDataProvider
5258
methods: Method[],
5359
databaseItem: DatabaseItem,
5460
hideModeledMethods: boolean,
61+
mode: Mode,
5562
modeledMethods: Record<string, ModeledMethod[]>,
5663
modifiedMethodSignatures: Set<string>,
5764
): Promise<void> {
5865
if (
5966
this.methods !== methods ||
6067
this.databaseItem !== databaseItem ||
6168
this.hideModeledMethods !== hideModeledMethods ||
69+
this.mode !== mode ||
6270
this.modeledMethods !== modeledMethods ||
6371
this.modifiedMethodSignatures !== modifiedMethodSignatures
6472
) {
6573
this.methods = methods;
74+
this.sortedMethods = sortMethodsInGroups(methods, mode);
6675
this.databaseItem = databaseItem;
6776
this.sourceLocationPrefix =
6877
await this.databaseItem.getSourceLocationPrefix(this.cliServer);
6978
this.hideModeledMethods = hideModeledMethods;
79+
this.mode = mode;
7080
this.modeledMethods = modeledMethods;
7181
this.modifiedMethodSignatures = modifiedMethodSignatures;
7282

@@ -133,9 +143,9 @@ export class MethodsUsageDataProvider
133143
getChildren(item?: MethodsUsageTreeViewItem): MethodsUsageTreeViewItem[] {
134144
if (item === undefined) {
135145
if (this.hideModeledMethods) {
136-
return this.methods.filter((api) => !api.supported);
146+
return this.sortedMethods.filter((api) => !api.supported);
137147
} else {
138-
return this.methods;
148+
return this.sortedMethods;
139149
}
140150
} else if (isExternalApiUsage(item)) {
141151
return item.usages;
@@ -183,3 +193,15 @@ function usagesAreEqual(u1: Usage, u2: Usage): boolean {
183193
u1.url.endColumn === u2.url.endColumn
184194
);
185195
}
196+
197+
function sortMethodsInGroups(methods: Method[], mode: Mode): Method[] {
198+
const grouped = groupMethods(methods, mode);
199+
200+
const sortedGroupNames = sortGroupNames(grouped);
201+
202+
return sortedGroupNames.flatMap((groupName) => {
203+
const group = grouped[groupName];
204+
205+
return sortMethods(group);
206+
});
207+
}

extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { DatabaseItem } from "../../databases/local-databases";
99
import { CodeQLCliServer } from "../../codeql-cli/cli";
1010
import { ModelingStore } from "../modeling-store";
1111
import { ModeledMethod } from "../modeled-method";
12+
import { Mode } from "../shared/mode";
1213

1314
export class MethodsUsagePanel extends DisposableObject {
1415
private readonly dataProvider: MethodsUsageDataProvider;
@@ -34,13 +35,15 @@ export class MethodsUsagePanel extends DisposableObject {
3435
methods: Method[],
3536
databaseItem: DatabaseItem,
3637
hideModeledMethods: boolean,
38+
mode: Mode,
3739
modeledMethods: Record<string, ModeledMethod[]>,
3840
modifiedMethodSignatures: Set<string>,
3941
): Promise<void> {
4042
await this.dataProvider.setState(
4143
methods,
4244
databaseItem,
4345
hideModeledMethods,
46+
mode,
4447
modeledMethods,
4548
modifiedMethodSignatures,
4649
);
@@ -83,6 +86,14 @@ export class MethodsUsagePanel extends DisposableObject {
8386
}),
8487
);
8588

89+
this.push(
90+
this.modelingStore.onModeChanged(async (event) => {
91+
if (event.isActiveDb) {
92+
await this.handleStateChangeEvent();
93+
}
94+
}),
95+
);
96+
8697
this.push(
8798
this.modelingStore.onModifiedMethodsChanged(async (event) => {
8899
if (event.isActiveDb) {
@@ -99,6 +110,7 @@ export class MethodsUsagePanel extends DisposableObject {
99110
activeState.methods,
100111
activeState.databaseItem,
101112
activeState.hideModeledMethods,
113+
activeState.mode,
102114
activeState.modeledMethods,
103115
activeState.modifiedMethodSignatures,
104116
);

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

-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { dir } from "tmp-promise";
1414
import { isQueryLanguage } from "../common/query-language";
1515
import { DisposableObject } from "../common/disposable-object";
1616
import { MethodsUsagePanel } from "./methods-usage/methods-usage-panel";
17-
import { Mode } from "./shared/mode";
1817
import { Method, Usage } from "./method";
1918
import { setUpPack } from "./model-editor-queries";
2019
import { MethodModelingPanel } from "./method-modeling/method-modeling-panel";
@@ -204,7 +203,6 @@ export class ModelEditorModule extends DisposableObject {
204203
queryDir,
205204
db,
206205
modelFile,
207-
Mode.Application,
208206
);
209207

210208
this.modelingStore.onDbClosed(async (dbUri) => {

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

+15-9
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { Method } from "./method";
3535
import { ModeledMethod } from "./modeled-method";
3636
import { ExtensionPack } from "./shared/extension-pack";
3737
import { ModelConfigListener } from "../config";
38-
import { Mode } from "./shared/mode";
38+
import { INITIAL_MODE, Mode } from "./shared/mode";
3939
import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs";
4040
import { pickExtensionPack } from "./extension-pack-picker";
4141
import { getLanguageDisplayName } from "../common/query-language";
@@ -66,11 +66,11 @@ export class ModelEditorView extends AbstractWebview<
6666
private readonly queryDir: string,
6767
private readonly databaseItem: DatabaseItem,
6868
private readonly extensionPack: ExtensionPack,
69-
private mode: Mode,
69+
initialMode: Mode = INITIAL_MODE,
7070
) {
7171
super(app);
7272

73-
this.modelingStore.initializeStateForDb(databaseItem);
73+
this.modelingStore.initializeStateForDb(databaseItem, initialMode);
7474
this.registerToModelingStoreEvents();
7575
this.registerToModelConfigEvents();
7676

@@ -214,6 +214,7 @@ export class ModelEditorView extends AbstractWebview<
214214
this.databaseItem,
215215
msg.methodSignatures,
216216
);
217+
const mode = this.modelingStore.getMode(this.databaseItem);
217218

218219
await withProgress(
219220
async (progress) => {
@@ -227,7 +228,7 @@ export class ModelEditorView extends AbstractWebview<
227228
this.databaseItem.language,
228229
methods,
229230
modeledMethods,
230-
this.mode,
231+
mode,
231232
this.cliServer,
232233
this.app.logger,
233234
);
@@ -288,7 +289,7 @@ export class ModelEditorView extends AbstractWebview<
288289
);
289290
break;
290291
case "switchMode":
291-
this.mode = msg.mode;
292+
this.modelingStore.setMode(this.databaseItem, msg.mode);
292293
this.modelingStore.setMethods(this.databaseItem, []);
293294
await Promise.all([
294295
this.postMessage({
@@ -372,7 +373,7 @@ export class ModelEditorView extends AbstractWebview<
372373
showFlowGeneration: this.modelConfig.flowGeneration,
373374
showLlmButton,
374375
showMultipleModels: this.modelConfig.showMultipleModels,
375-
mode: this.mode,
376+
mode: this.modelingStore.getMode(this.databaseItem),
376377
},
377378
});
378379
}
@@ -398,9 +399,11 @@ export class ModelEditorView extends AbstractWebview<
398399
}
399400

400401
protected async loadMethods(progress: ProgressCallback): Promise<void> {
402+
const mode = this.modelingStore.getMode(this.databaseItem);
403+
401404
try {
402405
const cancellationTokenSource = new CancellationTokenSource();
403-
const queryResult = await runExternalApiQueries(this.mode, {
406+
const queryResult = await runExternalApiQueries(mode, {
404407
cliServer: this.cliServer,
405408
queryRunner: this.queryRunner,
406409
databaseItem: this.databaseItem,
@@ -434,11 +437,13 @@ export class ModelEditorView extends AbstractWebview<
434437
async (progress) => {
435438
const tokenSource = new CancellationTokenSource();
436439

440+
const mode = this.modelingStore.getMode(this.databaseItem);
441+
437442
let addedDatabase: DatabaseItem | undefined;
438443

439444
// In application mode, we need the database of a specific library to generate
440445
// the modeled methods. In framework mode, we'll use the current database.
441-
if (this.mode === Mode.Application) {
446+
if (mode === Mode.Application) {
442447
addedDatabase = await this.promptChooseNewOrExistingDatabase(
443448
progress,
444449
);
@@ -503,11 +508,12 @@ export class ModelEditorView extends AbstractWebview<
503508
this.databaseItem,
504509
methodSignatures,
505510
);
511+
const mode = this.modelingStore.getMode(this.databaseItem);
506512
await this.autoModeler.startModeling(
507513
packageName,
508514
methods,
509515
modeledMethods,
510-
this.mode,
516+
mode,
511517
);
512518
}
513519

extensions/ql-vscode/src/model-editor/modeling-store.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { DatabaseItem } from "../databases/local-databases";
55
import { Method, Usage } from "./method";
66
import { ModeledMethod } from "./modeled-method";
77
import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "./shared/hide-modeled-methods";
8+
import { INITIAL_MODE, Mode } from "./shared/mode";
89

910
interface DbModelingState {
1011
databaseItem: DatabaseItem;
1112
methods: Method[];
1213
hideModeledMethods: boolean;
14+
mode: Mode;
1315
modeledMethods: Record<string, ModeledMethod[]>;
1416
modifiedMethodSignatures: Set<string>;
1517
selectedMethod: Method | undefined;
@@ -27,6 +29,11 @@ interface HideModeledMethodsChangedEvent {
2729
isActiveDb: boolean;
2830
}
2931

32+
interface ModeChangedEvent {
33+
mode: Mode;
34+
isActiveDb: boolean;
35+
}
36+
3037
interface ModeledMethodsChangedEvent {
3138
modeledMethods: Record<string, ModeledMethod[]>;
3239
dbUri: string;
@@ -53,6 +60,7 @@ export class ModelingStore extends DisposableObject {
5360
public readonly onDbClosed: AppEvent<string>;
5461
public readonly onMethodsChanged: AppEvent<MethodsChangedEvent>;
5562
public readonly onHideModeledMethodsChanged: AppEvent<HideModeledMethodsChangedEvent>;
63+
public readonly onModeChanged: AppEvent<ModeChangedEvent>;
5664
public readonly onModeledMethodsChanged: AppEvent<ModeledMethodsChangedEvent>;
5765
public readonly onModifiedMethodsChanged: AppEvent<ModifiedMethodsChangedEvent>;
5866
public readonly onSelectedMethodChanged: AppEvent<SelectedMethodChangedEvent>;
@@ -65,6 +73,7 @@ export class ModelingStore extends DisposableObject {
6573
private readonly onDbClosedEventEmitter: AppEventEmitter<string>;
6674
private readonly onMethodsChangedEventEmitter: AppEventEmitter<MethodsChangedEvent>;
6775
private readonly onHideModeledMethodsChangedEventEmitter: AppEventEmitter<HideModeledMethodsChangedEvent>;
76+
private readonly onModeChangedEventEmitter: AppEventEmitter<ModeChangedEvent>;
6877
private readonly onModeledMethodsChangedEventEmitter: AppEventEmitter<ModeledMethodsChangedEvent>;
6978
private readonly onModifiedMethodsChangedEventEmitter: AppEventEmitter<ModifiedMethodsChangedEvent>;
7079
private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter<SelectedMethodChangedEvent>;
@@ -98,6 +107,11 @@ export class ModelingStore extends DisposableObject {
98107
this.onHideModeledMethodsChanged =
99108
this.onHideModeledMethodsChangedEventEmitter.event;
100109

110+
this.onModeChangedEventEmitter = this.push(
111+
app.createEventEmitter<ModeChangedEvent>(),
112+
);
113+
this.onModeChanged = this.onModeChangedEventEmitter.event;
114+
101115
this.onModeledMethodsChangedEventEmitter = this.push(
102116
app.createEventEmitter<ModeledMethodsChangedEvent>(),
103117
);
@@ -117,12 +131,16 @@ export class ModelingStore extends DisposableObject {
117131
this.onSelectedMethodChangedEventEmitter.event;
118132
}
119133

120-
public initializeStateForDb(databaseItem: DatabaseItem) {
134+
public initializeStateForDb(
135+
databaseItem: DatabaseItem,
136+
mode: Mode = INITIAL_MODE,
137+
) {
121138
const dbUri = databaseItem.databaseUri.toString();
122139
this.state.set(dbUri, {
123140
databaseItem,
124141
methods: [],
125142
hideModeledMethods: INITIAL_HIDE_MODELED_METHODS_VALUE,
143+
mode,
126144
modeledMethods: {},
127145
modifiedMethodSignatures: new Set(),
128146
selectedMethod: undefined,
@@ -214,6 +232,22 @@ export class ModelingStore extends DisposableObject {
214232
});
215233
}
216234

235+
public setMode(dbItem: DatabaseItem, mode: Mode) {
236+
const dbState = this.getState(dbItem);
237+
const dbUri = dbItem.databaseUri.toString();
238+
239+
dbState.mode = mode;
240+
241+
this.onModeChangedEventEmitter.fire({
242+
mode,
243+
isActiveDb: dbUri === this.activeDb,
244+
});
245+
}
246+
247+
public getMode(dbItem: DatabaseItem) {
248+
return this.getState(dbItem).mode;
249+
}
250+
217251
/**
218252
* Returns the modeled methods for the given database item and method signatures.
219253
* If the `methodSignatures` argument is not provided or is undefined, returns all modeled methods.

extensions/ql-vscode/src/model-editor/shared/mode.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ export enum Mode {
22
Application = "application",
33
Framework = "framework",
44
}
5+
6+
export const INITIAL_MODE = Mode.Application;

extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export function createMockModelingStore({
88
onDbClosed = jest.fn(),
99
onMethodsChanged = jest.fn(),
1010
onHideModeledMethodsChanged = jest.fn(),
11+
onModeChanged = jest.fn(),
1112
onModeledMethodsChanged = jest.fn(),
1213
onModifiedMethodsChanged = jest.fn(),
1314
}: {
@@ -17,6 +18,7 @@ export function createMockModelingStore({
1718
onDbClosed?: ModelingStore["onDbClosed"];
1819
onMethodsChanged?: ModelingStore["onMethodsChanged"];
1920
onHideModeledMethodsChanged?: ModelingStore["onHideModeledMethodsChanged"];
21+
onModeChanged?: ModelingStore["onModeChanged"];
2022
onModeledMethodsChanged?: ModelingStore["onModeledMethodsChanged"];
2123
onModifiedMethodsChanged?: ModelingStore["onModifiedMethodsChanged"];
2224
} = {}): ModelingStore {
@@ -27,6 +29,7 @@ export function createMockModelingStore({
2729
onDbClosed,
2830
onMethodsChanged,
2931
onHideModeledMethodsChanged,
32+
onModeChanged,
3033
onModeledMethodsChanged,
3134
onModifiedMethodsChanged,
3235
});

0 commit comments

Comments
 (0)