Skip to content

Commit dd1ced9

Browse files
committed
Merge branch 'koesie10/selected-copy' into koesie10/export-results-sorting-filtering
2 parents d6ae5b1 + 4ed409d commit dd1ced9

36 files changed

+668
-74
lines changed

extensions/ql-vscode/src/common/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ import { AppEventEmitter } from './events';
22

33
export interface App {
44
createEventEmitter<T>(): AppEventEmitter<T>;
5+
extensionPath: string;
6+
globalStoragePath: string;
7+
workspaceStoragePath?: string;
58
}

extensions/ql-vscode/src/common/vscode/vscode-app.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
1+
import * as vscode from 'vscode';
12
import { App } from '../app';
23
import { AppEventEmitter } from '../events';
34
import { VSCodeAppEventEmitter } from './events';
45

56
export class ExtensionApp implements App {
7+
public constructor(
8+
public readonly extensionContext: vscode.ExtensionContext
9+
) {
10+
}
11+
12+
public get extensionPath(): string {
13+
return this.extensionContext.extensionPath;
14+
}
15+
16+
public get globalStoragePath(): string {
17+
return this.extensionContext.globalStorageUri.fsPath;
18+
}
19+
20+
public get workspaceStoragePath(): string | undefined {
21+
return this.extensionContext.storageUri?.fsPath;
22+
}
23+
624
public createEventEmitter<T>(): AppEventEmitter<T> {
725
return new VSCodeAppEventEmitter<T>();
826
}

extensions/ql-vscode/src/databases/db-config-store.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as chokidar from 'chokidar';
55
import { DisposableObject } from '../pure/disposable-object';
66
import { DbConfigValidator } from './db-config-validator';
77
import { ValueResult } from '../common/value-result';
8+
import { App } from '../common/app';
89

910
export class DbConfigStore extends DisposableObject {
1011
private readonly configPath: string;
@@ -14,17 +15,16 @@ export class DbConfigStore extends DisposableObject {
1415
private configErrors: string[];
1516
private configWatcher: chokidar.FSWatcher | undefined;
1617

17-
public constructor(
18-
workspaceStoragePath: string,
19-
extensionPath: string) {
18+
public constructor(app: App) {
2019
super();
2120

22-
this.configPath = path.join(workspaceStoragePath, 'workspace-databases.json');
21+
const storagePath = app.workspaceStoragePath || app.globalStoragePath;
22+
this.configPath = path.join(storagePath, 'workspace-databases.json');
2323

2424
this.config = this.createEmptyConfig();
2525
this.configErrors = [];
2626
this.configWatcher = undefined;
27-
this.configValidator = new DbConfigValidator(extensionPath);
27+
this.configValidator = new DbConfigValidator(app.extensionPath);
2828
}
2929

3030
public async initialize(): Promise<void> {
@@ -99,7 +99,11 @@ export class DbConfigStore extends DisposableObject {
9999
repositoryLists: [],
100100
owners: [],
101101
repositories: [],
102-
}
102+
},
103+
local: {
104+
lists: [],
105+
databases: [],
106+
},
103107
};
104108
}
105109
}

extensions/ql-vscode/src/databases/db-config.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
export interface DbConfig {
44
remote: RemoteDbConfig;
5+
local: LocalDbConfig;
56
}
67

78
export interface RemoteDbConfig {
@@ -15,6 +16,23 @@ export interface RemoteRepositoryList {
1516
repositories: string[];
1617
}
1718

19+
export interface LocalDbConfig {
20+
lists: LocalList[];
21+
databases: LocalDatabase[];
22+
}
23+
24+
export interface LocalList {
25+
name: string;
26+
databases: LocalDatabase[];
27+
}
28+
29+
export interface LocalDatabase {
30+
name: string;
31+
dateAdded: number;
32+
language: string;
33+
storagePath: string;
34+
}
35+
1836
export function cloneDbConfig(config: DbConfig): DbConfig {
1937
return {
2038
remote: {
@@ -24,6 +42,13 @@ export function cloneDbConfig(config: DbConfig): DbConfig {
2442
})),
2543
owners: [...config.remote.owners],
2644
repositories: [...config.remote.repositories],
27-
}
45+
},
46+
local: {
47+
lists: config.local.lists.map((list) => ({
48+
name: list.name,
49+
databases: list.databases.map((db) => ({ ...db })),
50+
})),
51+
databases: config.local.databases.map((db) => ({ ...db })),
52+
},
2853
};
2954
}

extensions/ql-vscode/src/databases/db-module.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as vscode from 'vscode';
2+
import { ExtensionApp } from '../common/vscode/vscode-app';
23
import { isCanary, isNewQueryRunExperienceEnabled } from '../config';
34
import { logger } from '../logging';
45
import { DisposableObject } from '../pure/disposable-object';
@@ -21,9 +22,9 @@ export class DbModule extends DisposableObject {
2122

2223
void logger.log('Initializing database module');
2324

24-
const storagePath = extensionContext.storageUri?.fsPath || extensionContext.globalStorageUri.fsPath;
25-
const extensionPath = extensionContext.extensionPath;
26-
const dbConfigStore = new DbConfigStore(storagePath, extensionPath);
25+
const app = new ExtensionApp(extensionContext);
26+
27+
const dbConfigStore = new DbConfigStore(app);
2728
await dbConfigStore.initialize();
2829

2930
const dbManager = new DbManager(dbConfigStore);

extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ import {
1515
*/
1616
export class DbTreeViewItem extends vscode.TreeItem {
1717
constructor(
18+
// iconPath and tooltip must have those names because
19+
// they are part of the vscode.TreeItem interface
20+
1821
public readonly dbItem: DbItem | undefined,
19-
public readonly icon: vscode.ThemeIcon | undefined,
22+
public readonly iconPath: vscode.ThemeIcon | undefined,
2023
public readonly label: string,
2124
public readonly tooltip: string | undefined,
2225
public readonly collapsibleState: vscode.TreeItemCollapsibleState,

extensions/ql-vscode/src/extension.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ import { createVariantAnalysisContentProvider } from './remote-queries/variant-a
120120
import { VSCodeMockGitHubApiServer } from './mocks/vscode-mock-gh-api-server';
121121
import { VariantAnalysisResultsManager } from './remote-queries/variant-analysis-results-manager';
122122
import { initializeDbModule } from './databases/db-module';
123+
import { RepositoriesFilterSortStateWithIds } from './pure/variant-analysis-filter-sort';
123124

124125
/**
125126
* extension.ts
@@ -949,8 +950,8 @@ async function activateWithInstalledDistribution(
949950
);
950951

951952
ctx.subscriptions.push(
952-
commandRunner('codeQL.copyVariantAnalysisRepoList', async (variantAnalysisId: number) => {
953-
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysisId);
953+
commandRunner('codeQL.copyVariantAnalysisRepoList', async (variantAnalysisId: number, filterSort?: RepositoriesFilterSortStateWithIds) => {
954+
await variantAnalysisManager.copyRepoListToClipboard(variantAnalysisId, filterSort);
954955
})
955956
);
956957

extensions/ql-vscode/src/pure/interface-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
VariantAnalysisScannedRepositoryResult,
88
VariantAnalysisScannedRepositoryState,
99
} from '../remote-queries/shared/variant-analysis';
10+
import { RepositoriesFilterSortStateWithIds } from './variant-analysis-filter-sort';
1011

1112
/**
1213
* This module contains types and code that are shared between
@@ -474,6 +475,7 @@ export interface OpenQueryTextMessage {
474475

475476
export interface CopyRepositoryListMessage {
476477
t: 'copyRepositoryList';
478+
filterSort?: RepositoriesFilterSortStateWithIds;
477479
}
478480

479481
export interface ExportResultsMessage {

extensions/ql-vscode/src/view/variant-analysis/filterSort.ts renamed to extensions/ql-vscode/src/pure/variant-analysis-filter-sort.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Repository, RepositoryWithMetadata } from '../../remote-queries/shared/repository';
2-
import { parseDate } from '../../pure/date';
1+
import { Repository, RepositoryWithMetadata } from '../remote-queries/shared/repository';
2+
import { parseDate } from './date';
33

44
export enum SortKey {
55
Name = 'name',
@@ -13,6 +13,10 @@ export type RepositoriesFilterSortState = {
1313
sortKey: SortKey;
1414
}
1515

16+
export type RepositoriesFilterSortStateWithIds = RepositoriesFilterSortState & {
17+
repositoryIds?: number[];
18+
}
19+
1620
export const defaultFilterSortState: RepositoriesFilterSortState = {
1721
searchValue: '',
1822
sortKey: SortKey.Name,
@@ -52,7 +56,7 @@ export function compareRepository(filterSortState: RepositoriesFilterSortState |
5256
}
5357

5458
type SortableResult = {
55-
repository: SortableRepository;
59+
repository: SortableRepository & Pick<Repository, 'id'>;
5660
resultCount?: number;
5761
}
5862

@@ -71,3 +75,31 @@ export function compareWithResults(filterSortState: RepositoriesFilterSortState
7175
return fallbackSort(left.repository, right.repository);
7276
};
7377
}
78+
79+
function hasRepositoryIds(filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): filterSortState is RepositoriesFilterSortStateWithIds {
80+
if (!filterSortState) {
81+
return false;
82+
}
83+
84+
return 'repositoryIds' in filterSortState;
85+
}
86+
87+
function isFilterOnRepositoryIds(filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): filterSortState is RepositoriesFilterSortStateWithIds & Required<Pick<RepositoriesFilterSortStateWithIds, 'repositoryIds'>> {
88+
return hasRepositoryIds(filterSortState) && filterSortState.repositoryIds !== undefined && filterSortState.repositoryIds.length > 0;
89+
}
90+
91+
// These define the behavior for undefined input values
92+
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[], filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): T[];
93+
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): T[] | undefined;
94+
95+
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(repositories: T[] | undefined, filterSortState: RepositoriesFilterSortState | RepositoriesFilterSortStateWithIds | undefined): T[] | undefined {
96+
if (!repositories) {
97+
return undefined;
98+
}
99+
100+
const filteredRepositories = isFilterOnRepositoryIds(filterSortState) ?
101+
repositories.filter(repo => filterSortState.repositoryIds.includes(repo.repository.id)) :
102+
repositories.filter(repo => matchesFilter(repo.repository, filterSortState));
103+
104+
return filteredRepositories.sort(compareWithResults(filterSortState));
105+
}

extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ import * as os from 'os';
3232
import { cancelVariantAnalysis } from './gh-api/gh-actions-api-client';
3333
import { ProgressCallback, UserCancellationException } from '../commandRunner';
3434
import { CodeQLCliServer } from '../cli';
35+
import {
36+
defaultFilterSortState,
37+
filterAndSortRepositoriesWithResults,
38+
RepositoriesFilterSortStateWithIds,
39+
} from '../pure/variant-analysis-filter-sort';
3540

3641
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
3742
private static readonly REPO_STATES_FILENAME = 'repo_states.json';
@@ -389,13 +394,15 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
389394
await cancelVariantAnalysis(credentials, variantAnalysis);
390395
}
391396

392-
public async copyRepoListToClipboard(variantAnalysisId: number) {
397+
public async copyRepoListToClipboard(variantAnalysisId: number, filterSort: RepositoriesFilterSortStateWithIds = defaultFilterSortState) {
393398
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
394399
if (!variantAnalysis) {
395400
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
396401
}
397402

398-
const fullNames = variantAnalysis.scannedRepos?.filter(a => a.resultCount && a.resultCount > 0).map(a => a.repository.fullName);
403+
const filteredRepositories = filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos ?? [], filterSort);
404+
405+
const fullNames = filteredRepositories.filter(a => a.resultCount && a.resultCount > 0).map(a => a.repository.fullName);
399406
if (!fullNames || fullNames.length === 0) {
400407
return;
401408
}

0 commit comments

Comments
 (0)