Skip to content

Commit 0835b14

Browse files
authored
Merge pull request #2948 from github/koesie10/sort-usages-panel
Sort methods in usages panel according to model editor sort order
2 parents 0d70022 + 78284cb commit 0835b14

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";
@@ -224,7 +223,6 @@ export class ModelEditorModule extends DisposableObject {
224223
queryDir,
225224
db,
226225
modelFile,
227-
Mode.Application,
228226
);
229227

230228
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";
@@ -63,11 +63,11 @@ export class ModelEditorView extends AbstractWebview<
6363
private readonly queryDir: string,
6464
private readonly databaseItem: DatabaseItem,
6565
private readonly extensionPack: ExtensionPack,
66-
private mode: Mode,
66+
initialMode: Mode = INITIAL_MODE,
6767
) {
6868
super(app);
6969

70-
this.modelingStore.initializeStateForDb(databaseItem);
70+
this.modelingStore.initializeStateForDb(databaseItem, initialMode);
7171
this.registerToModelingStoreEvents();
7272
this.registerToModelConfigEvents();
7373

@@ -211,6 +211,7 @@ export class ModelEditorView extends AbstractWebview<
211211
this.databaseItem,
212212
msg.methodSignatures,
213213
);
214+
const mode = this.modelingStore.getMode(this.databaseItem);
214215

215216
await withProgress(
216217
async (progress) => {
@@ -224,7 +225,7 @@ export class ModelEditorView extends AbstractWebview<
224225
this.databaseItem.language,
225226
methods,
226227
modeledMethods,
227-
this.mode,
228+
mode,
228229
this.cliServer,
229230
this.app.logger,
230231
);
@@ -285,7 +286,7 @@ export class ModelEditorView extends AbstractWebview<
285286
);
286287
break;
287288
case "switchMode":
288-
this.mode = msg.mode;
289+
this.modelingStore.setMode(this.databaseItem, msg.mode);
289290
this.modelingStore.setMethods(this.databaseItem, []);
290291
await Promise.all([
291292
this.postMessage({
@@ -376,7 +377,7 @@ export class ModelEditorView extends AbstractWebview<
376377
showFlowGeneration: this.modelConfig.flowGeneration,
377378
showLlmButton,
378379
showMultipleModels: this.modelConfig.showMultipleModels,
379-
mode: this.mode,
380+
mode: this.modelingStore.getMode(this.databaseItem),
380381
sourceArchiveAvailable,
381382
},
382383
});
@@ -403,9 +404,11 @@ export class ModelEditorView extends AbstractWebview<
403404
}
404405

405406
protected async loadMethods(progress: ProgressCallback): Promise<void> {
407+
const mode = this.modelingStore.getMode(this.databaseItem);
408+
406409
try {
407410
const cancellationTokenSource = new CancellationTokenSource();
408-
const queryResult = await runExternalApiQueries(this.mode, {
411+
const queryResult = await runExternalApiQueries(mode, {
409412
cliServer: this.cliServer,
410413
queryRunner: this.queryRunner,
411414
databaseItem: this.databaseItem,
@@ -439,11 +442,13 @@ export class ModelEditorView extends AbstractWebview<
439442
async (progress) => {
440443
const tokenSource = new CancellationTokenSource();
441444

445+
const mode = this.modelingStore.getMode(this.databaseItem);
446+
442447
let addedDatabase: DatabaseItem | undefined;
443448

444449
// In application mode, we need the database of a specific library to generate
445450
// the modeled methods. In framework mode, we'll use the current database.
446-
if (this.mode === Mode.Application) {
451+
if (mode === Mode.Application) {
447452
addedDatabase = await this.promptChooseNewOrExistingDatabase(
448453
progress,
449454
);
@@ -508,11 +513,12 @@ export class ModelEditorView extends AbstractWebview<
508513
this.databaseItem,
509514
methodSignatures,
510515
);
516+
const mode = this.modelingStore.getMode(this.databaseItem);
511517
await this.autoModeler.startModeling(
512518
packageName,
513519
methods,
514520
modeledMethods,
515-
this.mode,
521+
mode,
516522
);
517523
}
518524

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)