Merge pull request #2894 from github/starcke/lang-context-history
Add support for filtering history panel.
This commit is contained in:
@@ -871,6 +871,7 @@ async function activateWithInstalledDistribution(
|
||||
ctx,
|
||||
queryHistoryConfigurationListener,
|
||||
labelProvider,
|
||||
languageContext,
|
||||
async (
|
||||
from: CompletedLocalQueryInfo,
|
||||
to: CompletedLocalQueryInfo,
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user