Merge pull request #2894 from github/starcke/lang-context-history

Add support for filtering history panel.
This commit is contained in:
Anders Starcke Henriksen
2023-10-04 13:38:38 +02:00
committed by GitHub
9 changed files with 144 additions and 43 deletions

View File

@@ -871,6 +871,7 @@ async function activateWithInstalledDistribution(
ctx,
queryHistoryConfigurationListener,
labelProvider,
languageContext,
async (
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo,

View File

@@ -10,9 +10,10 @@ import {
} from "vscode";
import { DisposableObject } from "../common/disposable-object";
import { assertNever } from "../common/helpers-pure";
import { QueryHistoryInfo } from "./query-history-info";
import { getLanguage, QueryHistoryInfo } from "./query-history-info";
import { QueryStatus } from "./query-status";
import { HistoryItemLabelProvider } from "./history-item-label-provider";
import { LanguageContextStore } from "../language-context-store";
export enum SortOrder {
NameAsc = "NameAsc",
@@ -50,7 +51,10 @@ export class HistoryTreeDataProvider
private current: QueryHistoryInfo | undefined;
constructor(private readonly labelProvider: HistoryItemLabelProvider) {
constructor(
private readonly labelProvider: HistoryItemLabelProvider,
private readonly languageContext: LanguageContextStore,
) {
super();
}
@@ -127,51 +131,55 @@ export class HistoryTreeDataProvider
getChildren(element?: QueryHistoryInfo): ProviderResult<QueryHistoryInfo[]> {
return element
? []
: this.history.sort((h1, h2) => {
const h1Label = this.labelProvider.getLabel(h1).toLowerCase();
const h2Label = this.labelProvider.getLabel(h2).toLowerCase();
: this.history
.filter((h) => {
return this.languageContext.shouldInclude(getLanguage(h));
})
.sort((h1, h2) => {
const h1Label = this.labelProvider.getLabel(h1).toLowerCase();
const h2Label = this.labelProvider.getLabel(h2).toLowerCase();
const h1Date = this.getItemDate(h1);
const h1Date = this.getItemDate(h1);
const h2Date = this.getItemDate(h2);
const h2Date = this.getItemDate(h2);
const resultCount1 =
h1.t === "local"
? h1.completedQuery?.resultCount ?? -1
: h1.resultCount ?? -1;
const resultCount2 =
h2.t === "local"
? h2.completedQuery?.resultCount ?? -1
: h2.resultCount ?? -1;
const resultCount1 =
h1.t === "local"
? h1.completedQuery?.resultCount ?? -1
: h1.resultCount ?? -1;
const resultCount2 =
h2.t === "local"
? h2.completedQuery?.resultCount ?? -1
: h2.resultCount ?? -1;
switch (this.sortOrder) {
case SortOrder.NameAsc:
return h1Label.localeCompare(h2Label, env.language);
switch (this.sortOrder) {
case SortOrder.NameAsc:
return h1Label.localeCompare(h2Label, env.language);
case SortOrder.NameDesc:
return h2Label.localeCompare(h1Label, env.language);
case SortOrder.NameDesc:
return h2Label.localeCompare(h1Label, env.language);
case SortOrder.DateAsc:
return h1Date - h2Date;
case SortOrder.DateAsc:
return h1Date - h2Date;
case SortOrder.DateDesc:
return h2Date - h1Date;
case SortOrder.DateDesc:
return h2Date - h1Date;
case SortOrder.CountAsc:
// If the result counts are equal, sort by name.
return resultCount1 - resultCount2 === 0
? h1Label.localeCompare(h2Label, env.language)
: resultCount1 - resultCount2;
case SortOrder.CountAsc:
// If the result counts are equal, sort by name.
return resultCount1 - resultCount2 === 0
? h1Label.localeCompare(h2Label, env.language)
: resultCount1 - resultCount2;
case SortOrder.CountDesc:
// If the result counts are equal, sort by name.
return resultCount2 - resultCount1 === 0
? h2Label.localeCompare(h1Label, env.language)
: resultCount2 - resultCount1;
default:
assertNever(this.sortOrder);
}
});
case SortOrder.CountDesc:
// If the result counts are equal, sort by name.
return resultCount2 - resultCount1 === 0
? h2Label.localeCompare(h1Label, env.language)
: resultCount2 - resultCount1;
default:
assertNever(this.sortOrder);
}
});
}
getParent(_element: QueryHistoryInfo): ProviderResult<QueryHistoryInfo> {

View File

@@ -62,6 +62,7 @@ import {
showAndLogInformationMessage,
showAndLogWarningMessage,
} from "../common/logging";
import { LanguageContextStore } from "../language-context-store";
/**
* query-history-manager.ts
@@ -141,6 +142,7 @@ export class QueryHistoryManager extends DisposableObject {
ctx: ExtensionContext,
private readonly queryHistoryConfigListener: QueryHistoryConfig,
private readonly labelProvider: HistoryItemLabelProvider,
private readonly languageContext: LanguageContextStore,
private readonly doCompareCallback: (
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo,
@@ -158,7 +160,7 @@ export class QueryHistoryManager extends DisposableObject {
);
this.treeDataProvider = this.push(
new HistoryTreeDataProvider(this.labelProvider),
new HistoryTreeDataProvider(this.labelProvider, this.languageContext),
);
this.treeView = this.push(
window.createTreeView("codeQLQueryHistory", {
@@ -230,6 +232,12 @@ export class QueryHistoryManager extends DisposableObject {
this.registerQueryHistoryScrubber(queryHistoryConfigListener, this, ctx);
this.registerToVariantAnalysisEvents();
this.push(
this.languageContext.onLanguageContextChanged(async () => {
this.treeDataProvider.refresh();
}),
);
}
public getCommands(): QueryHistoryCommands {

View File

@@ -7,6 +7,7 @@ import {
import { CancellationTokenSource } from "vscode";
import { QueryResultType } from "../../../src/query-server/legacy-messages";
import { QueryMetadata } from "../../../src/common/interface-types";
import { QueryLanguage } from "../../../src/common/query-language";
export function createMockLocalQueryInfo({
startTime = new Date(),
@@ -16,6 +17,7 @@ export function createMockLocalQueryInfo({
dbName = "db-name",
hasMetadata = false,
queryWithResults = undefined,
language = undefined,
}: {
startTime?: Date;
resultCount?: number;
@@ -24,6 +26,7 @@ export function createMockLocalQueryInfo({
dbName?: string;
hasMetadata?: boolean;
queryWithResults?: QueryWithResults | undefined;
language?: QueryLanguage;
}): LocalQueryInfo {
const cancellationToken = {
dispose: () => {
@@ -40,6 +43,7 @@ export function createMockLocalQueryInfo({
databaseInfo: {
databaseUri: "databaseUri",
name: dbName,
language,
},
start: startTime,
id: faker.number.int().toString(),

View File

@@ -5,6 +5,7 @@ import {
VariantAnalysisStatus,
} from "../../../src/variant-analysis/shared/variant-analysis";
import { createMockVariantAnalysis } from "../variant-analysis/shared/variant-analysis";
import { QueryLanguage } from "../../../src/common/query-language";
export function createMockVariantAnalysisHistoryItem({
historyItemStatus = QueryStatus.InProgress,
@@ -14,6 +15,7 @@ export function createMockVariantAnalysisHistoryItem({
userSpecifiedLabel = undefined,
executionStartTime = undefined,
variantAnalysis = undefined,
language = QueryLanguage.Javascript,
}: {
historyItemStatus?: QueryStatus;
variantAnalysisStatus?: VariantAnalysisStatus;
@@ -22,6 +24,7 @@ export function createMockVariantAnalysisHistoryItem({
userSpecifiedLabel?: string;
executionStartTime?: number;
variantAnalysis?: VariantAnalysis;
language?: QueryLanguage;
}): VariantAnalysisHistoryItem {
return {
t: "variant-analysis",
@@ -34,6 +37,7 @@ export function createMockVariantAnalysisHistoryItem({
createMockVariantAnalysis({
status: variantAnalysisStatus,
executionStartTime,
language,
}),
userSpecifiedLabel,
};

View File

@@ -15,11 +15,13 @@ export function createMockVariantAnalysis({
scannedRepos = createMockScannedRepos(),
skippedRepos = createMockSkippedRepos(),
executionStartTime = faker.number.int(),
language = QueryLanguage.Javascript,
}: {
status?: VariantAnalysisStatus;
scannedRepos?: VariantAnalysisScannedRepository[];
skippedRepos?: VariantAnalysisSkippedRepositories;
executionStartTime?: number | undefined;
language?: QueryLanguage;
}): VariantAnalysis {
return {
id: faker.number.int(),
@@ -32,7 +34,7 @@ export function createMockVariantAnalysis({
query: {
name: "a-query-name",
filePath: "a-query-file-path",
language: QueryLanguage.Javascript,
language,
text: "a-query-text",
},
databases: {

View File

@@ -28,9 +28,13 @@ import {
import { QueryHistoryManager } from "../../../../src/query-history/query-history-manager";
import { createMockQueryHistoryDirs } from "../../../factories/query-history/query-history-dirs";
import { createMockApp } from "../../../__mocks__/appMock";
import { LanguageContextStore } from "../../../../src/language-context-store";
import { App } from "../../../../src/common/app";
import { QueryLanguage } from "../../../../src/common/query-language";
describe("HistoryTreeDataProvider", () => {
const mockExtensionLocation = join(tmpDir.name, "mock-extension-location");
let app: App;
let configListener: QueryHistoryConfigListener;
const doCompareCallback = jest.fn();
@@ -45,10 +49,12 @@ describe("HistoryTreeDataProvider", () => {
let historyTreeDataProvider: HistoryTreeDataProvider;
let labelProvider: HistoryItemLabelProvider;
let languageContext: LanguageContextStore;
beforeEach(() => {
jest.spyOn(extLogger, "log").mockResolvedValue(undefined);
app = createMockApp({});
configListener = new QueryHistoryConfigListener();
localQueriesResultsViewStub = {
showResults: jest.fn(),
@@ -124,7 +130,11 @@ describe("HistoryTreeDataProvider", () => {
ttlInMillis: 0,
onDidChangeConfiguration: jest.fn(),
});
historyTreeDataProvider = new HistoryTreeDataProvider(labelProvider);
languageContext = new LanguageContextStore(app);
historyTreeDataProvider = new HistoryTreeDataProvider(
labelProvider,
languageContext,
);
});
afterEach(async () => {
@@ -418,11 +428,68 @@ describe("HistoryTreeDataProvider", () => {
expect(children).toEqual(expected);
});
});
describe("filtering", () => {
const history = [
createMockLocalQueryInfo({
userSpecifiedLabel: "a",
// No language at all => unknown
}),
createMockVariantAnalysisHistoryItem({
userSpecifiedLabel: "b",
// No specified language => javascript
}),
createMockLocalQueryInfo({
userSpecifiedLabel: "c",
language: QueryLanguage.Python,
}),
createMockVariantAnalysisHistoryItem({
userSpecifiedLabel: "d",
language: QueryLanguage.Java,
}),
];
let treeDataProvider: HistoryTreeDataProvider;
beforeEach(async () => {
queryHistoryManager = await createMockQueryHistory(allHistory);
(queryHistoryManager.treeDataProvider as any).history = [...history];
treeDataProvider = queryHistoryManager.treeDataProvider;
});
it("should get all if no filter is provided", async () => {
const expected = [history[0], history[1], history[2], history[3]];
treeDataProvider.sortOrder = SortOrder.NameAsc;
const children = await treeDataProvider.getChildren();
expect(children).toEqual(expected);
});
it("should filter local runs by language", async () => {
const expected = [history[3]];
treeDataProvider.sortOrder = SortOrder.NameAsc;
await languageContext.setLanguageContext(QueryLanguage.Java);
const children = await treeDataProvider.getChildren();
expect(children).toEqual(expected);
});
it("should filter variant analysis runs by language", async () => {
const expected = [history[2]];
treeDataProvider.sortOrder = SortOrder.NameAsc;
await languageContext.setLanguageContext(QueryLanguage.Python);
const children = await treeDataProvider.getChildren();
expect(children).toEqual(expected);
});
});
});
async function createMockQueryHistory(allHistory: QueryHistoryInfo[]) {
const qhm = new QueryHistoryManager(
createMockApp({}),
app,
{} as QueryRunner,
{} as DatabaseManager,
localQueriesResultsViewStub,
@@ -439,6 +506,7 @@ describe("HistoryTreeDataProvider", () => {
ttlInMillis: 0,
onDidChangeConfiguration: jest.fn(),
}),
languageContext,
doCompareCallback,
);
(qhm.treeDataProvider as any).history = [...allHistory];

View File

@@ -28,6 +28,7 @@ import { createMockQueryHistoryDirs } from "../../../factories/query-history/que
import { createMockApp } from "../../../__mocks__/appMock";
import { App } from "../../../../src/common/app";
import { createMockCommandManager } from "../../../__mocks__/commandsMock";
import { LanguageContextStore } from "../../../../src/language-context-store";
describe("QueryHistoryManager", () => {
const mockExtensionLocation = join(tmpDir.name, "mock-extension-location");
@@ -937,6 +938,7 @@ describe("QueryHistoryManager", () => {
ttlInMillis: 0,
onDidChangeConfiguration: jest.fn(),
}),
new LanguageContextStore(mockApp),
doCompareCallback,
);
(qhm.treeDataProvider as any).history = [...allHistory];

View File

@@ -23,6 +23,7 @@ import { QueryHistoryManager } from "../../../../src/query-history/query-history
import { mockedObject } from "../../utils/mocking.helpers";
import { createMockQueryHistoryDirs } from "../../../factories/query-history/query-history-dirs";
import { createMockApp } from "../../../__mocks__/appMock";
import { LanguageContextStore } from "../../../../src/language-context-store";
// set a higher timeout since recursive delete may take a while, expecially on Windows.
jest.setTimeout(120000);
@@ -74,8 +75,10 @@ describe("Variant Analyses and QueryHistoryManager", () => {
join(STORAGE_DIR, "workspace-query-history.json"),
).queries;
const app = createMockApp({});
qhm = new QueryHistoryManager(
createMockApp({}),
app,
{} as QueryRunner,
{} as DatabaseManager,
localQueriesResultsViewStub,
@@ -97,6 +100,7 @@ describe("Variant Analyses and QueryHistoryManager", () => {
ttlInMillis: 0,
onDidChangeConfiguration: jest.fn(),
}),
new LanguageContextStore(app),
asyncNoop,
);
disposables.push(qhm);