You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

317 lines
8.3 KiB

import type { Disposable, Event, ExtensionContext, SecretStorageChangeEvent } from 'vscode';
import { EventEmitter } from 'vscode';
import type { ViewShowBranchComparison } from './config';
import type { StoredSearchQuery } from './git/search';
import type { Subscription } from './subscription';
import { debug } from './system/decorators/log';
import type { TrackedUsage, TrackedUsageKeys } from './usageTracker';
import type { CompletedActions } from './webviews/home/protocol';
export type StorageChangeEvent =
| {
/**
* The key of the stored value that has changed.
*/
readonly key: GlobalStoragePath;
readonly workspace: false;
}
| {
/**
* The key of the stored value that has changed.
*/
readonly key: WorkspaceStoragePath;
readonly workspace: true;
};
export class Storage implements Disposable {
private _onDidChange = new EventEmitter<StorageChangeEvent>();
get onDidChange(): Event<StorageChangeEvent> {
return this._onDidChange.event;
}
private _onDidChangeSecrets = new EventEmitter<SecretStorageChangeEvent>();
get onDidChangeSecrets(): Event<SecretStorageChangeEvent> {
return this._onDidChangeSecrets.event;
}
private readonly _disposable: Disposable;
constructor(private readonly context: ExtensionContext) {
this._disposable = this.context.secrets.onDidChange(e => this._onDidChangeSecrets.fire(e));
}
dispose(): void {
this._disposable.dispose();
}
get<T extends GlobalStoragePath>(key: T): GlobalStoragePathValue<T>;
get<T extends GlobalStoragePath>(
key: T,
defaultValue: NonNullable<GlobalStoragePathValue<T>>,
): NonNullable<GlobalStoragePathValue<T>>;
@debug({ logThreshold: 50 })
get<T extends GlobalStoragePath>(
key: T,
defaultValue?: GlobalStoragePathValue<T>,
): GlobalStoragePathValue<T> | undefined {
return this.context.globalState.get(`gitlens:${key}`, defaultValue);
}
@debug({ logThreshold: 250 })
async delete<T extends GlobalStoragePath>(key: T): Promise<void> {
await this.context.globalState.update(`gitlens:${key}`, undefined);
this._onDidChange.fire({ key: key, workspace: false });
}
@debug({ args: { 1: false }, logThreshold: 250 })
async store<T extends GlobalStoragePath>(key: T, value: GlobalStoragePathValue<T>): Promise<void> {
await this.context.globalState.update(`gitlens:${key}`, value);
this._onDidChange.fire({ key: key, workspace: false });
}
@debug({ args: false, logThreshold: 250 })
async getSecret(key: SecretKeys): Promise<string | undefined> {
return this.context.secrets.get(key);
}
@debug({ args: false, logThreshold: 250 })
async deleteSecret(key: SecretKeys): Promise<void> {
return this.context.secrets.delete(key);
}
@debug({ args: false, logThreshold: 250 })
async storeSecret(key: SecretKeys, value: string): Promise<void> {
return this.context.secrets.store(key, value);
}
getWorkspace<T extends WorkspaceStoragePath>(key: T): WorkspaceStoragePathValue<T>;
getWorkspace<T extends WorkspaceStoragePath>(
key: T,
defaultValue: NonNullable<WorkspaceStoragePathValue<T>>,
): NonNullable<WorkspaceStoragePathValue<T>>;
@debug({ logThreshold: 25 })
getWorkspace<T extends WorkspaceStoragePath>(
key: T,
defaultValue?: WorkspaceStoragePathValue<T>,
): WorkspaceStoragePathValue<T> | undefined {
return this.context.workspaceState.get(`gitlens:${key}`, defaultValue);
}
@debug({ logThreshold: 250 })
async deleteWorkspace<T extends WorkspaceStoragePath>(key: T): Promise<void> {
await this.context.workspaceState.update(`gitlens:${key}`, undefined);
this._onDidChange.fire({ key: key, workspace: true });
}
@debug({ args: { 1: false }, logThreshold: 250 })
async storeWorkspace<T extends WorkspaceStoragePath>(key: T, value: WorkspaceStoragePathValue<T>): Promise<void> {
await this.context.workspaceState.update(`gitlens:${key}`, value);
this._onDidChange.fire({ key: key, workspace: true });
}
}
export type SecretKeys = string;
export const enum DeprecatedStorageKeys {
/** @deprecated */
DisallowConnectionPrefix = 'gitlens:disallow:connection:',
}
export const enum SyncedStorageKeys {
Version = 'gitlens:synced:version',
PreReleaseVersion = 'gitlens:synced:preVersion',
HomeViewWelcomeVisible = 'gitlens:views:welcome:visible',
}
export interface GlobalStorage {
avatars?: [string, StoredAvatar][];
provider: {
authentication: {
skip: Record<string, boolean>;
};
};
home: {
actions: {
completed?: CompletedActions[];
};
steps: {
completed?: string[];
};
sections: {
dismissed?: string[];
};
};
pendingWelcomeOnFocus?: boolean;
pendingWhatsNewOnFocus?: boolean;
plus: {
migratedAuthentication?: boolean;
discountNotificationShown?: boolean;
};
// Don't change this key name ('premium`) as its the stored subscription
premium: {
subscription?: Stored<Subscription>;
};
synced: {
version?: string;
// Keep the pre-release version separate from the released version
preVersion?: string;
};
usages?: Record<TrackedUsageKeys, TrackedUsage>;
version?: string;
// Keep the pre-release version separate from the released version
preVersion?: string;
views: {
welcome: {
visible?: boolean;
};
};
}
export interface WorkspaceStorage {
assumeRepositoriesOnStartup?: boolean;
branch: {
comparisons?: StoredBranchComparisons;
};
connected: Record<string, boolean>;
gitComandPalette: {
usage?: RecentUsage;
};
gitPath?: string;
graph: {
banners: {
dismissed?: Record<string, boolean>;
};
columns?: Record<string, StoredGraphColumn>;
hiddenRefs?: Record<string, StoredGraphHiddenRef>;
};
remote: {
default?: string;
};
starred: {
branches?: StoredStarred;
repositories?: StoredStarred;
};
views: {
repositories: {
autoRefresh?: boolean;
};
searchAndCompare: {
keepResults?: boolean;
pinned?: StoredPinnedItems;
};
commitDetails: {
autolinksExpanded?: boolean;
dismissed?: string[];
};
};
pinned: {
/** @deprecated use `gitlens:views:searchAndCompare:pinned` */
comparisons?: DeprecatedPinnedComparisons;
};
}
export interface Stored<T, SchemaVersion extends number = 1> {
v: SchemaVersion;
data: T;
}
export interface StoredAvatar {
uri: string;
timestamp: number;
}
export interface StoredBranchComparison {
ref: string;
notation: '..' | '...' | undefined;
type: Exclude<ViewShowBranchComparison, false> | undefined;
}
export interface StoredBranchComparisons {
[id: string]: string | StoredBranchComparison;
}
export interface StoredGraphColumn {
isHidden?: boolean;
width?: number;
}
export type StoredGraphRefType = 'head' | 'remote' | 'tag';
export interface StoredGraphHiddenRef {
id: string;
type: StoredGraphRefType;
name: string;
owner?: string;
}
export interface StoredNamedRef {
label?: string;
ref: string;
}
export interface StoredPinnedComparison {
type: 'comparison';
timestamp: number;
path: string;
ref1: StoredNamedRef;
ref2: StoredNamedRef;
notation?: '..' | '...';
}
export interface StoredPinnedSearch {
type: 'search';
timestamp: number;
path: string;
labels: {
label: string;
queryLabel:
| string
| {
label: string;
resultsType?: { singular: string; plural: string };
};
};
search: StoredSearchQuery;
}
export type StoredPinnedItem = StoredPinnedComparison | StoredPinnedSearch;
export type StoredPinnedItems = Record<string, StoredPinnedItem>;
export type StoredStarred = Record<string, boolean>;
export type RecentUsage = Record<string, number>;
interface DeprecatedPinnedComparison {
path: string;
ref1: StoredNamedRef;
ref2: StoredNamedRef;
notation?: '..' | '...';
}
interface DeprecatedPinnedComparisons {
[id: string]: DeprecatedPinnedComparison;
}
type SubPath<T, Key extends keyof T> = Key extends string
? T[Key] extends Record<string, any>
?
| `${Key}:${SubPath<T[Key], Exclude<keyof T[Key], keyof any[]>>}`
| `${Key}:${Exclude<keyof T[Key], keyof any[]> & string}`
: never
: never;
type Path<T> = SubPath<T, keyof T> | keyof T;
type PathValue<T, P extends Path<T>> = P extends `${infer Key}:${infer Rest}`
? Key extends keyof T
? Rest extends Path<T[Key]>
? PathValue<T[Key], Rest>
: never
: never
: P extends keyof T
? T[P]
: never;
type GlobalStoragePath = Path<GlobalStorage>;
type GlobalStoragePathValue<P extends GlobalStoragePath> = PathValue<GlobalStorage, P>;
type WorkspaceStoragePath = Path<WorkspaceStorage>;
type WorkspaceStoragePathValue<P extends WorkspaceStoragePath> = PathValue<WorkspaceStorage, P>;