diff --git a/src/annotations/annotations.ts b/src/annotations/annotations.ts index 509e7db..fa07f26 100644 --- a/src/annotations/annotations.ts +++ b/src/annotations/annotations.ts @@ -83,9 +83,9 @@ export async function getHeatmapColors() { const disposable = configuration.onDidChange(e => { if ( - configuration.changed(e, 'heatmap', 'ageThreshold') || - configuration.changed(e, 'heatmap', 'hotColor') || - configuration.changed(e, 'heatmap', 'coldColor') + configuration.changed(e, 'heatmap.ageThreshold') || + configuration.changed(e, 'heatmap.hotColor') || + configuration.changed(e, 'heatmap.coldColor') ) { disposable.dispose(); heatmapColors = undefined; diff --git a/src/annotations/fileAnnotationController.ts b/src/annotations/fileAnnotationController.ts index 4f9b24f..7256887 100644 --- a/src/annotations/fileAnnotationController.ts +++ b/src/annotations/fileAnnotationController.ts @@ -102,7 +102,7 @@ export class FileAnnotationController implements Disposable { private onConfigurationChanged(e: ConfigurationChangeEvent) { const cfg = Container.config; - if (configuration.changed(e, 'blame', 'highlight')) { + if (configuration.changed(e, 'blame.highlight')) { Decorations.gutterBlameHighlight?.dispose(); Decorations.gutterBlameHighlight = undefined; @@ -136,7 +136,7 @@ export class FileAnnotationController implements Disposable { } } - if (configuration.changed(e, 'changes', 'locations')) { + if (configuration.changed(e, 'changes.locations')) { Decorations.changesLineAddedAnnotation?.dispose(); Decorations.changesLineChangedAnnotation?.dispose(); Decorations.changesLineDeletedAnnotation?.dispose(); @@ -191,21 +191,21 @@ export class FileAnnotationController implements Disposable { const initializing = configuration.initializing(e); - if (configuration.changed(e, 'blame', 'toggleMode')) { + if (configuration.changed(e, 'blame.toggleMode')) { this._toggleModes.set(FileAnnotationType.Blame, cfg.blame.toggleMode); if (!initializing && cfg.blame.toggleMode === AnnotationsToggleMode.File) { void this.clearAll(); } } - if (configuration.changed(e, 'changes', 'toggleMode')) { + if (configuration.changed(e, 'changes.toggleMode')) { this._toggleModes.set(FileAnnotationType.Changes, cfg.changes.toggleMode); if (!initializing && cfg.changes.toggleMode === AnnotationsToggleMode.File) { void this.clearAll(); } } - if (configuration.changed(e, 'heatmap', 'toggleMode')) { + if (configuration.changed(e, 'heatmap.toggleMode')) { this._toggleModes.set(FileAnnotationType.Heatmap, cfg.heatmap.toggleMode); if (!initializing && cfg.heatmap.toggleMode === AnnotationsToggleMode.File) { void this.clearAll(); diff --git a/src/annotations/lineAnnotationController.ts b/src/annotations/lineAnnotationController.ts index 85a679b..0be87a2 100644 --- a/src/annotations/lineAnnotationController.ts +++ b/src/annotations/lineAnnotationController.ts @@ -53,7 +53,7 @@ export class LineAnnotationController implements Disposable { private onConfigurationChanged(e: ConfigurationChangeEvent) { if (!configuration.changed(e, 'currentLine')) return; - if (configuration.changed(e, 'currentLine', 'enabled')) { + if (configuration.changed(e, 'currentLine.enabled')) { if (Container.config.currentLine.enabled) { this._enabled = true; this.resume(); diff --git a/src/commands/gitCommands.actions.ts b/src/commands/gitCommands.actions.ts index 5f5be10..3dc9020 100644 --- a/src/commands/gitCommands.actions.ts +++ b/src/commands/gitCommands.actions.ts @@ -151,7 +151,7 @@ export namespace GitActions { }, ) { if ( - !configuration.get('views', branch.remote ? 'remotes' : 'branches', 'reveal') || + !configuration.get(`views.${branch.remote ? 'remotes' : 'branches'}.reveal` as const) || (Container.repositoriesView.visible && !(branch.remote ? Container.remotesView.visible : Container.branchesView.visible)) ) { @@ -647,7 +647,7 @@ export namespace GitActions { }, ) { if ( - !configuration.get('views', 'commits', 'reveal') || + !configuration.get('views.commits.reveal') || (Container.repositoriesView.visible && !Container.commitsView.visible) ) { return Container.repositoriesView.revealCommit(commit, options); @@ -710,7 +710,7 @@ export namespace GitActions { }, ) { if ( - !configuration.get('views', 'tags', 'reveal') || + !configuration.get('views.tags.reveal') || (Container.repositoriesView.visible && !Container.tagsView.visible) ) { return Container.repositoriesView.revealTag(tag, options); @@ -780,7 +780,7 @@ export namespace GitActions { }, ) { // if ( - // configuration.get('views', 'repositories', 'enabled') && + // configuration.get('views.repositories.enabled') && // (Container.repositoriesView.visible || !Container.remotesView.visible) // ) { // return Container.repositoriesView.revealRemote(remote, options); @@ -835,7 +835,7 @@ export namespace GitActions { }, ) { if ( - !configuration.get('views', 'stashes', 'reveal') || + !configuration.get('views.stashes.reveal') || (Container.repositoriesView.visible && !Container.stashesView.visible) ) { return Container.repositoriesView.revealStash(stash, options); diff --git a/src/commands/gitCommands.ts b/src/commands/gitCommands.ts index ec84b5a..5f2cd45 100644 --- a/src/commands/gitCommands.ts +++ b/src/commands/gitCommands.ts @@ -238,7 +238,7 @@ export class GitCommandsCommand extends Command { const willConfirmToggle = new QuickCommandButtons.WillConfirmToggle(command.confirm(), async () => { if (command?.skipConfirmKey == null) return; - const skipConfirmations = configuration.get('gitCommands', 'skipConfirmations') ?? []; + const skipConfirmations = configuration.get('gitCommands.skipConfirmations') ?? []; const index = skipConfirmations.indexOf(command.skipConfirmKey); if (index !== -1) { @@ -247,7 +247,7 @@ export class GitCommandsCommand extends Command { skipConfirmations.push(command.skipConfirmKey); } - void (await configuration.updateEffective('gitCommands', 'skipConfirmations', skipConfirmations)); + void (await configuration.updateEffective('gitCommands.skipConfirmations', skipConfirmations)); }); buttons.push(willConfirmToggle); } else { @@ -284,9 +284,7 @@ export class GitCommandsCommand extends Command { private async showInputStep(step: QuickInputStep, commandsStep: PickCommandStep) { const input = window.createInputBox(); - input.ignoreFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut') - ? true - : step.ignoreFocusOut ?? false; + input.ignoreFocusOut = !configuration.get('gitCommands.closeOnFocusOut') ? true : step.ignoreFocusOut ?? false; const disposables: Disposable[] = []; @@ -398,7 +396,7 @@ export class GitCommandsCommand extends Command { } private async showPickStep(step: QuickPickStep, commandsStep: PickCommandStep) { - const originalIgnoreFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut') + const originalIgnoreFocusOut = !configuration.get('gitCommands.closeOnFocusOut') ? true : step.ignoreFocusOut ?? false; const originalStepIgnoreFocusOut = step.ignoreFocusOut; diff --git a/src/commands/quickCommand.steps.ts b/src/commands/quickCommand.steps.ts index fff07b1..f481949 100644 --- a/src/commands/quickCommand.steps.ts +++ b/src/commands/quickCommand.steps.ts @@ -805,7 +805,7 @@ export async function* pickCommitStep< value: typeof picked === 'string' && log?.count === 0 ? picked : undefined, items: showInSideBarCommand != null ? [showInSideBarCommand, ...getItems(log)] : getItems(log), onDidLoadMore: async quickpick => { - log = await log?.more?.(configuration.get('advanced', 'maxListItems')); + log = await log?.more?.(configuration.get('advanced.maxListItems')); onDidLoadMore?.(log); if (typeof placeholder !== 'string') { quickpick.placeholder = placeholder(context, log); @@ -949,7 +949,7 @@ export function* pickCommitsStep< matchOnDetail: true, items: getItems(log), onDidLoadMore: async quickpick => { - log = await log?.more?.(configuration.get('advanced', 'maxListItems')); + log = await log?.more?.(configuration.get('advanced.maxListItems')); onDidLoadMore?.(log); if (typeof placeholder !== 'string') { quickpick.placeholder = placeholder(context, log); diff --git a/src/commands/resetSuppressedWarnings.ts b/src/commands/resetSuppressedWarnings.ts index cb3713e..fffdfeb 100644 --- a/src/commands/resetSuppressedWarnings.ts +++ b/src/commands/resetSuppressedWarnings.ts @@ -10,6 +10,6 @@ export class ResetSuppressedWarningsCommand extends Command { } async execute() { - void (await configuration.update('advanced', 'messages', undefined, ConfigurationTarget.Global)); + void (await configuration.update('advanced.messages', undefined, ConfigurationTarget.Global)); } } diff --git a/src/commands/switchMode.ts b/src/commands/switchMode.ts index 2de0a54..d7c20aa 100644 --- a/src/commands/switchMode.ts +++ b/src/commands/switchMode.ts @@ -29,16 +29,16 @@ export class SwitchModeCommand extends Command { // Check if we have applied any annotations and clear them if we won't be applying them again if (active != null && active.length !== 0) { - const activeAnnotations = Container.config.modes[active].annotations; + const activeAnnotations = Container.config.modes?.[active].annotations; if (activeAnnotations != null) { - const newAnnotations = pick.key != null ? Container.config.modes[pick.key].annotations : undefined; + const newAnnotations = pick.key != null ? Container.config.modes?.[pick.key].annotations : undefined; if (activeAnnotations !== newAnnotations) { await Container.fileAnnotations.clearAll(); } } } - await configuration.update('mode', 'active', pick.key, ConfigurationTarget.Global); + await configuration.update('mode.active', pick.key, ConfigurationTarget.Global); } } @@ -50,10 +50,10 @@ export class ToggleReviewModeCommand extends Command { @log({ args: false, singleLine: true, timed: false }) async execute() { - if (!Object.keys(Container.config.modes).includes('review')) return; + if (Container.config.modes == null || !Object.keys(Container.config.modes).includes('review')) return; const mode = Container.config.mode.active === 'review' ? undefined : 'review'; - await configuration.update('mode', 'active', mode, ConfigurationTarget.Global); + await configuration.update('mode.active', mode, ConfigurationTarget.Global); } } @@ -65,9 +65,9 @@ export class ToggleZenModeCommand extends Command { @log({ args: false, singleLine: true, timed: false }) async execute() { - if (!Object.keys(Container.config.modes).includes('zen')) return; + if (Container.config.modes == null || !Object.keys(Container.config.modes).includes('zen')) return; const mode = Container.config.mode.active === 'zen' ? undefined : 'zen'; - await configuration.update('mode', 'active', mode, ConfigurationTarget.Global); + await configuration.update('mode.active', mode, ConfigurationTarget.Global); } } diff --git a/src/config.ts b/src/config.ts index f4b9343..c657727 100644 --- a/src/config.ts +++ b/src/config.ts @@ -109,7 +109,7 @@ export interface Config { alignment: 'left' | 'right'; }; }; - modes: Record; + modes: Record | null; outputLevel: TraceLevel; partners: Record< string, diff --git a/src/configuration.ts b/src/configuration.ts index 060a969..739edfa 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -8,7 +8,6 @@ import { Event, EventEmitter, ExtensionContext, - Uri, workspace, } from 'vscode'; import { Config } from './config'; @@ -74,68 +73,23 @@ export class Configuration { readonly initializingChangeEvent: ConfigurationChangeEvent = { affectsConfiguration: () => true }; get(): Config; - get(s1: S1, scope?: ConfigurationScope | null, defaultValue?: Config[S1]): Config[S1]; - get( - s1: S1, - s2: S2, + get( + section: T, scope?: ConfigurationScope | null, - defaultValue?: Config[S1][S2], - ): Config[S1][S2]; - get( - s1: S1, - s2: S2, - s3: S3, + defaultValue?: ConfigPathValue, + ): ConfigPathValue; + get( + section?: T, scope?: ConfigurationScope | null, - defaultValue?: Config[S1][S2][S3], - ): Config[S1][S2][S3]; - get< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >( - s1: S1, - s2: S2, - s3: S3, - s4: S4, - scope?: ConfigurationScope | null, - defaultValue?: Config[S1][S2][S3][S4], - ): Config[S1][S2][S3][S4]; - get(...args: any[]): T { - let section: string | undefined; - let scope: ConfigurationScope | null | undefined; - let defaultValue: T | undefined; - if (args.length > 0) { - section = args[0]; - if (typeof args[1] === 'string') { - section += `.${args[1]}`; - if (typeof args[2] === 'string') { - section += `.${args[2]}`; - if (typeof args[3] === 'string') { - section += `.${args[3]}`; - scope = args[4]; - defaultValue = args[5]; - } else { - scope = args[3]; - defaultValue = args[4]; - } - } else { - scope = args[2]; - defaultValue = args[3]; - } - } else { - scope = args[1]; - defaultValue = args[2]; - } - } - + defaultValue?: ConfigPathValue, + ): Config | ConfigPathValue { return defaultValue === undefined ? workspace .getConfiguration(section === undefined ? undefined : extensionId, scope) - .get(section === undefined ? extensionId : section)! + .get>(section === undefined ? extensionId : section)! : workspace .getConfiguration(section === undefined ? undefined : extensionId, scope) - .get(section === undefined ? extensionId : section, defaultValue)!; + .get>(section === undefined ? extensionId : section, defaultValue)!; } getAny(section: string, scope?: ConfigurationScope | null): T | undefined; @@ -146,46 +100,11 @@ export class Configuration { : workspace.getConfiguration(undefined, scope).get(section, defaultValue); } - changed(e: ConfigurationChangeEvent, s1: S1, scope?: ConfigurationScope | null): boolean; - changed( - e: ConfigurationChangeEvent, - s1: S1, - s2: S2, - scope?: ConfigurationScope | null, - ): boolean; - changed( + changed( e: ConfigurationChangeEvent, - s1: S1, - s2: S2, - s3: S3, - scope?: ConfigurationScope | null, - ): boolean; - changed< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >(e: ConfigurationChangeEvent, s1: S1, s2: S2, s3: S3, s4: S4, scope?: ConfigurationScope | null): boolean; - changed(e: ConfigurationChangeEvent, ...args: any[]) { - let section: string = args[0]; - let scope: ConfigurationScope | null | undefined; - if (typeof args[1] === 'string') { - section += `.${args[1]}`; - if (typeof args[2] === 'string') { - section += `.${args[2]}`; - if (typeof args[3] === 'string') { - section += args[3]; - scope = args[4]; - } else { - scope = args[3]; - } - } else { - scope = args[2]; - } - } else { - scope = args[1]; - } - + section: T, + scope?: ConfigurationScope | null | undefined, + ): boolean { return e.affectsConfiguration(`${extensionId}.${section}`, scope!); } @@ -193,55 +112,12 @@ export class Configuration { return e === this.initializingChangeEvent; } - inspect( - s1: S1, - scope?: ConfigurationScope | null, - ): ConfigInspection | undefined; - inspect( - s1: S1, - s2: S2, + inspect( + section: T, scope?: ConfigurationScope | null, - ): ConfigInspection | undefined; - inspect( - s1: S1, - s2: S2, - s3: S3, - scope?: ConfigurationScope | null, - ): ConfigInspection | undefined; - inspect< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >( - s1: S1, - s2: S2, - s3: S3, - s4: S4, - scope?: ConfigurationScope | null, - ): ConfigInspection | undefined; - inspect(...args: any[]) { - let section: string = args[0]; - let resource: Uri | null | undefined; - if (typeof args[1] === 'string') { - section += `.${args[1]}`; - if (typeof args[2] === 'string') { - section += `.${args[2]}`; - if (typeof args[3] === 'string') { - section += args[3]; - resource = args[4]; - } else { - resource = args[3]; - } - } else { - resource = args[2]; - } - } else { - resource = args[1]; - } - + ): ConfigInspection> | undefined { return workspace - .getConfiguration(section === undefined ? undefined : extensionId, resource) + .getConfiguration(section === undefined ? undefined : extensionId, scope) .inspect(section === undefined ? extensionId : section); } @@ -249,68 +125,18 @@ export class Configuration { return workspace.getConfiguration(undefined, scope).inspect(section); } - migrate( + async migrate( from: string, - to1: S1, - options: { fallbackValue?: Config[S1]; migrationFn?(value: any): Config[S1] }, - ): Promise; - migrate( - from: string, - to1: S1, - to2: S2, - options: { fallbackValue?: Config[S1][S2]; migrationFn?(value: any): Config[S1][S2] }, - ): Promise; - migrate( - from: string, - to1: S1, - to2: S2, - to3: S3, - options: { fallbackValue?: Config[S1][S2][S3]; migrationFn?(value: any): Config[S1][S2][S3] }, - ): Promise; - migrate< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >( - from: string, - to1: S1, - to2: S2, - to3: S3, - to4: S4, - options: { fallbackValue?: Config[S1][S2][S3][S4]; migrationFn?(value: any): Config[S1][S2][S3][S4] }, - ): Promise; - async migrate(from: string, ...args: any[]): Promise { - let to: string = args[0]; - let options: { fallbackValue?: any; migrationFn?(value: any): any } | undefined; - if (typeof args[1] === 'string' && args.length > 3) { - to += `.${args[1]}`; - if (typeof args[2] === 'string' && args.length > 4) { - to += `.${args[2]}`; - if (typeof args[3] === 'string' && args.length > 5) { - to += `.${args[3]}`; - options = args[4]; - } else { - options = args[3]; - } - } else { - options = args[2]; - } - } else { - options = args[1]; - } - - if (options === undefined) { - options = {}; - } - + to: T, + options: { fallbackValue?: ConfigPathValue; migrationFn?(value: any): ConfigPathValue }, + ): Promise { const inspection = configuration.inspect(from as any); if (inspection === undefined) return false; let migrated = false; if (inspection.globalValue !== undefined) { await this.update( - to as any, + to, options.migrationFn != null ? options.migrationFn(inspection.globalValue) : inspection.globalValue, ConfigurationTarget.Global, ); @@ -326,7 +152,7 @@ export class Configuration { if (inspection.workspaceValue !== undefined) { await this.update( - to as any, + to, options.migrationFn != null ? options.migrationFn(inspection.workspaceValue) : inspection.workspaceValue, @@ -344,7 +170,7 @@ export class Configuration { if (inspection.workspaceFolderValue !== undefined) { await this.update( - to as any, + to, options.migrationFn != null ? options.migrationFn(inspection.workspaceFolderValue) : inspection.workspaceFolderValue, @@ -361,77 +187,26 @@ export class Configuration { } if (!migrated && options.fallbackValue !== undefined) { - await this.update(to as any, options.fallbackValue, ConfigurationTarget.Global); + await this.update(to, options.fallbackValue, ConfigurationTarget.Global); migrated = true; } return migrated; } - migrateIfMissing( - from: string, - to1: S1, - options: { migrationFn?(value: any): Config[S1] }, - ): Promise; - migrateIfMissing( - from: string, - to1: S1, - to2: S2, - options: { migrationFn?(value: any): Config[S1][S2] }, - ): Promise; - migrateIfMissing( - from: string, - to1: S1, - to2: S2, - to3: S3, - options: { migrationFn?(value: any): Config[S1][S2][S3] }, - ): Promise; - migrateIfMissing< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >( + async migrateIfMissing( from: string, - to1: S1, - to2: S2, - to3: S3, - to4: S4, - options: { migrationFn?(value: any): Config[S1][S2][S3][S4] }, - ): Promise; - async migrateIfMissing(from: string, ...args: any[]): Promise { - let to: string = args[0]; - let options: { migrationFn?(value: any): any } | undefined; - if (typeof args[1] === 'string' && args.length > 3) { - to += `.${args[1]}`; - if (typeof args[2] === 'string' && args.length > 4) { - to += `.${args[2]}`; - if (typeof args[3] === 'string' && args.length > 5) { - to += `.${args[3]}`; - options = args[4]; - } else { - options = args[3]; - } - } else { - options = args[2]; - } - } else { - options = args[1]; - } - - if (options === undefined) { - options = {}; - } - - // async migrateIfMissing(from: string, to: string, options: { migrationFn?(value: TFrom): TTo } = {}) { + to: T, + options: { migrationFn?(value: any): ConfigPathValue }, + ): Promise { const fromInspection = configuration.inspect(from as any); if (fromInspection === undefined) return; - const toInspection = configuration.inspect(to as any); + const toInspection = configuration.inspect(to); if (fromInspection.globalValue !== undefined) { if (toInspection === undefined || toInspection.globalValue === undefined) { await this.update( - to as any, + to, options.migrationFn != null ? options.migrationFn(fromInspection.globalValue) : fromInspection.globalValue, @@ -450,7 +225,7 @@ export class Configuration { if (fromInspection.workspaceValue !== undefined) { if (toInspection === undefined || toInspection.workspaceValue === undefined) { await this.update( - to as any, + to, options.migrationFn != null ? options.migrationFn(fromInspection.workspaceValue) : fromInspection.workspaceValue, @@ -469,7 +244,7 @@ export class Configuration { if (fromInspection.workspaceFolderValue !== undefined) { if (toInspection === undefined || toInspection.workspaceFolderValue === undefined) { await this.update( - to as any, + to, options.migrationFn != null ? options.migrationFn(fromInspection.workspaceFolderValue) : fromInspection.workspaceFolderValue, @@ -486,76 +261,15 @@ export class Configuration { } } - name(s1: S1): string; - name(s1: S1, s2: S2): string; - name( - s1: S1, - s2: S2, - s3: S3, - ): string; - name< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >(s1: S1, s2: S2, s3: S3, s4: S4): string; - name(...args: string[]) { - return args.join('.'); + name(section: T): string { + return section; } - update(s1: S1, value: Config[S1] | undefined, target: ConfigurationTarget): Thenable; - update( - s1: S1, - s2: S2, - value: Config[S1][S2] | undefined, + update( + section: T, + value: ConfigPathValue | undefined, target: ConfigurationTarget, - ): Thenable; - - update( - s1: S1, - s2: S2, - s3: S3, - value: Config[S1][S2][S3] | undefined, - target: ConfigurationTarget, - ): Thenable; - update< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >( - s1: S1, - s2: S2, - s3: S3, - s4: S4, - value: Config[S1][S2][S3][S4] | undefined, - target: ConfigurationTarget, - ): Thenable; - update(...args: any[]) { - let section: string = args[0]; - let value; - let target: ConfigurationTarget; - if (typeof args[1] === 'string' && args.length > 3) { - section += `.${args[1]}`; - if (typeof args[2] === 'string' && args.length > 4) { - section += `.${args[2]}`; - if (typeof args[3] === 'string' && args.length > 5) { - section += `.${args[3]}`; - value = args[4]; - target = args[5]; - } else { - value = args[3]; - target = args[4]; - } - } else { - value = args[2]; - target = args[3]; - } - } else { - value = args[1]; - target = args[2]; - } - + ): Thenable { return workspace.getConfiguration(extensionId).update(section, value, target); } @@ -565,55 +279,18 @@ export class Configuration { .update(section, value, target); } - updateEffective(s1: S1, value: Config[S1]): Thenable; - updateEffective( - s1: S1, - s2: S2, - value: Config[S1][S2], - ): Thenable; - updateEffective( - s1: S1, - s2: S2, - s3: S3, - value: Config[S1][S2][S3], - ): Thenable; - updateEffective< - S1 extends keyof Config, - S2 extends keyof Config[S1], - S3 extends keyof Config[S1][S2], - S4 extends keyof Config[S1][S2][S3] - >(s1: S1, s2: S2, s3: S3, s4: S4, value: Config[S1][S2][S3][S4]): Thenable; - updateEffective(...args: any[]) { - let section: string = args[0]; - let value; - if (typeof args[1] === 'string' && args.length > 2) { - section += `.${args[1]}`; - if (typeof args[2] === 'string' && args.length > 3) { - section += `.${args[2]}`; - if (typeof args[3] === 'string' && args.length > 4) { - section += `.${args[3]}`; - value = args[4]; - } else { - value = args[3]; - } - } else { - value = args[2]; - } - } else { - value = args[1]; - } - - const inspect = configuration.inspect(section as any)!; + updateEffective(section: T, value: ConfigPathValue | undefined): Thenable { + const inspect = configuration.inspect(section)!; if (inspect.workspaceFolderValue !== undefined) { if (value === inspect.workspaceFolderValue) return Promise.resolve(undefined); - return configuration.update(section as any, value, ConfigurationTarget.WorkspaceFolder); + return configuration.update(section, value, ConfigurationTarget.WorkspaceFolder); } if (inspect.workspaceValue !== undefined) { if (value === inspect.workspaceValue) return Promise.resolve(undefined); - return configuration.update(section as any, value, ConfigurationTarget.Workspace); + return configuration.update(section, value, ConfigurationTarget.Workspace); } if (inspect.globalValue === value || (inspect.globalValue === undefined && value === inspect.defaultValue)) { @@ -621,7 +298,7 @@ export class Configuration { } return configuration.update( - section as any, + section, Objects.areEquivalent(value, inspect.defaultValue) ? undefined : value, ConfigurationTarget.Global, ); @@ -629,3 +306,28 @@ export class Configuration { } export const configuration = new Configuration(); + +type PathImpl = Key extends string + ? T[Key] extends Record + ? + | `${Key}.${PathImpl> & string}` + | `${Key}.${Exclude & string}` + : never + : never; + +type PathImpl2 = PathImpl | keyof T; + +type Path = PathImpl2 extends string | keyof T ? PathImpl2 : keyof T; + +type PathValue> = P extends `${infer Key}.${infer Rest}` + ? Key extends keyof T + ? Rest extends Path + ? PathValue + : never + : never + : P extends keyof T + ? T[P] + : never; + +type ConfigPath = Path; +type ConfigPathValue

= PathValue; diff --git a/src/container.ts b/src/container.ts index 15484b1..cf28463 100644 --- a/src/container.ts +++ b/src/container.ts @@ -94,7 +94,7 @@ export class Container { context.subscriptions.push( configuration.onDidChange(e => { - if (!configuration.changed(e, 'terminalLinks', 'enabled')) return; + if (!configuration.changed(e, 'terminalLinks.enabled')) return; this._terminalLinks?.dispose(); if (Container.config.terminalLinks.enabled) { @@ -356,7 +356,7 @@ export class Container { private static applyMode(config: Config) { if (!config.mode.active) return config; - const mode = config.modes[config.mode.active]; + const mode = config.modes?.[config.mode.active]; if (mode == null) return config; if (mode.annotations != null) { @@ -410,11 +410,11 @@ export class Container { this._configsAffectedByMode = [ `gitlens.${configuration.name('mode')}`, `gitlens.${configuration.name('modes')}`, - `gitlens.${configuration.name('blame', 'toggleMode')}`, - `gitlens.${configuration.name('changes', 'toggleMode')}`, + `gitlens.${configuration.name('blame.toggleMode')}`, + `gitlens.${configuration.name('changes.toggleMode')}`, `gitlens.${configuration.name('codeLens')}`, `gitlens.${configuration.name('currentLine')}`, - `gitlens.${configuration.name('heatmap', 'toggleMode')}`, + `gitlens.${configuration.name('heatmap.toggleMode')}`, `gitlens.${configuration.name('hovers')}`, `gitlens.${configuration.name('statusBar')}`, ]; diff --git a/src/git/gitService.ts b/src/git/gitService.ts index 2caf8f5..87f1e65 100644 --- a/src/git/gitService.ts +++ b/src/git/gitService.ts @@ -249,7 +249,7 @@ export class GitService implements Disposable { PullRequestDateFormatting.reset(); } - if (configuration.changed(e, 'views', 'contributors', 'showAllBranches')) { + if (configuration.changed(e, 'views.contributors.showAllBranches')) { this._contributorsCache.clear(); } } @@ -369,7 +369,7 @@ export class GitService implements Disposable { private async repositorySearch(folder: WorkspaceFolder): Promise { const cc = Logger.getCorrelationContext(); const { uri } = folder; - const depth = configuration.get('advanced', 'repositorySearchDepth', uri); + const depth = configuration.get('advanced.repositorySearchDepth', uri); Logger.log(cc, `searching (depth=${depth})...`); diff --git a/src/hovers/lineHoverController.ts b/src/hovers/lineHoverController.ts index 98edb58..6f3f1d6 100644 --- a/src/hovers/lineHoverController.ts +++ b/src/hovers/lineHoverController.ts @@ -37,10 +37,7 @@ export class LineHoverController implements Disposable { } private onConfigurationChanged(e: ConfigurationChangeEvent) { - if ( - !configuration.changed(e, 'hovers', 'enabled') && - !configuration.changed(e, 'hovers', 'currentLine', 'enabled') - ) { + if (!configuration.changed(e, 'hovers.enabled') && !configuration.changed(e, 'hovers.currentLine.enabled')) { return; } diff --git a/src/messages.ts b/src/messages.ts index 669783c..e52fd54 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -185,7 +185,7 @@ export class Messages { ): Promise { Logger.log(`ShowMessage(${type}, '${message}', ${suppressionKey}, ${JSON.stringify(dontShowAgain)})`); - if (suppressionKey !== undefined && configuration.get('advanced', 'messages', suppressionKey)) { + if (suppressionKey !== undefined && configuration.get(`advanced.messages.${suppressionKey}` as const)) { Logger.log( `ShowMessage(${type}, '${message}', ${suppressionKey}, ${JSON.stringify(dontShowAgain)}) skipped`, ); @@ -231,16 +231,16 @@ export class Messages { } private static suppressedMessage(suppressionKey: SuppressedMessages) { - const messages: Record = configuration.get('advanced', 'messages'); + const messages = configuration.get('advanced.messages'); messages[suppressionKey] = true; for (const [key, value] of Object.entries(messages)) { if (value !== true) { - messages[key] = undefined; + delete messages[key as keyof typeof messages]; } } - return configuration.update('advanced', 'messages', messages as any, ConfigurationTarget.Global); + return configuration.update('advanced.messages', messages, ConfigurationTarget.Global); } } diff --git a/src/quickpicks.ts b/src/quickpicks.ts index b5b2af3..d254312 100644 --- a/src/quickpicks.ts +++ b/src/quickpicks.ts @@ -3,7 +3,7 @@ import { configuration } from './configuration'; export function getQuickPickIgnoreFocusOut() { - return !configuration.get('advanced', 'quickPick', 'closeOnFocusOut'); + return !configuration.get('advanced.quickPick.closeOnFocusOut'); } export * from './quickpicks/quickPicksItems'; diff --git a/src/quickpicks/commitPicker.ts b/src/quickpicks/commitPicker.ts index c52bf62..2335d69 100644 --- a/src/quickpicks/commitPicker.ts +++ b/src/quickpicks/commitPicker.ts @@ -71,7 +71,7 @@ export namespace CommitPicker { quickpick.enabled = false; try { - log = await (await log)?.more?.(configuration.get('advanced', 'maxListItems')); + log = await (await log)?.more?.(configuration.get('advanced.maxListItems')); const items = getItems(log); let activeIndex = -1; diff --git a/src/quickpicks/modePicker.ts b/src/quickpicks/modePicker.ts index 4cf87c9..1bfa6f2 100644 --- a/src/quickpicks/modePicker.ts +++ b/src/quickpicks/modePicker.ts @@ -9,13 +9,16 @@ export interface ModesQuickPickItem extends QuickPickItem { export namespace ModePicker { export async function show(): Promise { - const modes = Object.keys(Container.config.modes); - if (modes.length === 0) return undefined; + if (Container.config.modes == null) return undefined; + + const modes = Container.config.modes; + const modeKeys = Object.keys(modes); + if (modeKeys.length === 0) return undefined; const mode = Container.config.mode.active; - const items = modes.map(key => { - const modeCfg = Container.config.modes[key]; + const items = modeKeys.map(key => { + const modeCfg = modes[key]; const item: ModesQuickPickItem = { label: `${mode === key ? '$(check)\u00a0\u00a0' : '\u00a0\u00a0\u00a0\u00a0\u00a0'}${ modeCfg.name @@ -28,7 +31,7 @@ export namespace ModePicker { if (mode) { items.splice(0, 0, { - label: `Exit ${Container.config.modes[mode].name} mode`, + label: `Exit ${modes[mode].name} mode`, key: undefined, }); } diff --git a/src/statusbar/statusBarController.ts b/src/statusbar/statusBarController.ts index 051aa13..5d59d93 100644 --- a/src/statusbar/statusBarController.ts +++ b/src/statusbar/statusBarController.ts @@ -44,7 +44,7 @@ export class StatusBarController implements Disposable { if (configuration.changed(e, 'mode')) { const mode = Container.config.mode.active && Container.config.mode.statusBar.enabled - ? Container.config.modes[Container.config.mode.active] + ? Container.config.modes?.[Container.config.mode.active] : undefined; if (mode?.statusBarItemName) { const alignment = @@ -52,7 +52,7 @@ export class StatusBarController implements Disposable { ? StatusBarAlignment.Right : StatusBarAlignment.Left; - if (configuration.changed(e, 'mode', 'statusBar', 'alignment')) { + if (configuration.changed(e, 'mode.statusBar.alignment')) { if (this._statusBarMode?.alignment !== alignment) { this._statusBarMode?.dispose(); this._statusBarMode = undefined; @@ -78,7 +78,7 @@ export class StatusBarController implements Disposable { const alignment = Container.config.statusBar.alignment !== 'left' ? StatusBarAlignment.Right : StatusBarAlignment.Left; - if (configuration.changed(e, 'statusBar', 'alignment')) { + if (configuration.changed(e, 'statusBar.alignment')) { if (this._statusBarBlame?.alignment !== alignment) { this._statusBarBlame?.dispose(); this._statusBarBlame = undefined; @@ -90,13 +90,13 @@ export class StatusBarController implements Disposable { window.createStatusBarItem(alignment, alignment === StatusBarAlignment.Right ? 1000 : 0); this._statusBarBlame.command = Container.config.statusBar.command; - if (configuration.changed(e, 'statusBar', 'enabled')) { + if (configuration.changed(e, 'statusBar.enabled')) { Container.lineTracker.start( this, Container.lineTracker.onDidChangeActiveLines(this.onActiveLinesChanged, this), ); } - } else if (configuration.changed(e, 'statusBar', 'enabled')) { + } else if (configuration.changed(e, 'statusBar.enabled')) { Container.lineTracker.stop(this); this._statusBarBlame?.dispose(); diff --git a/src/trackers/documentTracker.ts b/src/trackers/documentTracker.ts index cc8e2c5..6cdca8c 100644 --- a/src/trackers/documentTracker.ts +++ b/src/trackers/documentTracker.ts @@ -93,16 +93,15 @@ export class DocumentTracker implements Disposable { // Only rest the cached state if we aren't initializing if ( !configuration.initializing(e) && - (configuration.changed(e, 'blame', 'ignoreWhitespace') || - configuration.changed(e, 'advanced', 'caching', 'enabled')) + (configuration.changed(e, 'blame.ignoreWhitespace') || configuration.changed(e, 'advanced.caching.enabled')) ) { for (const d of this._documentMap.values()) { (await d).reset('config'); } } - if (configuration.changed(e, 'advanced', 'blame', 'delayAfterEdit')) { - this._dirtyIdleTriggerDelay = configuration.get('advanced', 'blame', 'delayAfterEdit'); + if (configuration.changed(e, 'advanced.blame.delayAfterEdit')) { + this._dirtyIdleTriggerDelay = configuration.get('advanced.blame.delayAfterEdit'); this._dirtyIdleTriggeredDebounced = undefined; } } diff --git a/src/views/branchesView.ts b/src/views/branchesView.ts index cf7f138..158e1e9 100644 --- a/src/views/branchesView.ts +++ b/src/views/branchesView.ts @@ -333,28 +333,26 @@ export class BranchesView extends ViewBase } private setLayout(layout: ViewBranchesLayout) { - return configuration.updateEffective('views', this.configKey, 'branches', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.branches.layout` as const, layout); } private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.files.layout` as const, layout); } private setShowAvatars(enabled: boolean) { - return configuration.updateEffective('views', this.configKey, 'avatars', enabled); + return configuration.updateEffective(`views.${this.configKey}.avatars` as const, enabled); } private setShowBranchComparison(enabled: boolean) { return configuration.updateEffective( - 'views', - this.configKey, - 'showBranchComparison', + `views.${this.configKey}.showBranchComparison` as const, enabled ? ViewShowBranchComparison.Branch : false, ); } private async setShowBranchPullRequest(enabled: boolean) { - await configuration.updateEffective('views', this.configKey, 'pullRequests', 'showForBranches', enabled); - await configuration.updateEffective('views', this.configKey, 'pullRequests', 'enabled', enabled); + await configuration.updateEffective(`views.${this.configKey}.pullRequests.showForBranches` as const, enabled); + await configuration.updateEffective(`views.${this.configKey}.pullRequests.enabled` as const, enabled); } } diff --git a/src/views/commitsView.ts b/src/views/commitsView.ts index 48c6218..2feae34 100644 --- a/src/views/commitsView.ts +++ b/src/views/commitsView.ts @@ -371,7 +371,7 @@ export class CommitsView extends ViewBase { } private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.files.layout` as const, layout); } private setMyCommitsOnly(enabled: boolean) { @@ -381,20 +381,18 @@ export class CommitsView extends ViewBase { } private setShowAvatars(enabled: boolean) { - return configuration.updateEffective('views', this.configKey, 'avatars', enabled); + return configuration.updateEffective(`views.${this.configKey}.avatars` as const, enabled); } private setShowBranchComparison(enabled: boolean) { return configuration.updateEffective( - 'views', - this.configKey, - 'showBranchComparison', + `views.${this.configKey}.showBranchComparison` as const, enabled ? ViewShowBranchComparison.Working : false, ); } private async setShowBranchPullRequest(enabled: boolean) { - await configuration.updateEffective('views', this.configKey, 'pullRequests', 'showForBranches', enabled); - await configuration.updateEffective('views', this.configKey, 'pullRequests', 'enabled', enabled); + await configuration.updateEffective(`views.${this.configKey}.pullRequests.showForBranches` as const, enabled); + await configuration.updateEffective(`views.${this.configKey}.pullRequests.enabled` as const, enabled); } } diff --git a/src/views/contributorsView.ts b/src/views/contributorsView.ts index 54a1212..690a43c 100644 --- a/src/views/contributorsView.ts +++ b/src/views/contributorsView.ts @@ -204,14 +204,14 @@ export class ContributorsView extends ViewBase impl repo.onDidChangeFileSystem(this.onFileSystemChanged, this), repo.startWatchingFileSystem(), configuration.onDidChange(e => { - if (configuration.changed(e, 'advanced', 'fileHistoryFollowsRenames')) { + if (configuration.changed(e, 'advanced.fileHistoryFollowsRenames')) { this.view.resetNodeLastKnownLimit(this); } }), diff --git a/src/views/remotesView.ts b/src/views/remotesView.ts index 574a255..cbe15b9 100644 --- a/src/views/remotesView.ts +++ b/src/views/remotesView.ts @@ -207,7 +207,7 @@ export class RemotesView extends ViewBase { !configuration.changed(e, 'defaultDateStyle') && !configuration.changed(e, 'defaultGravatarsStyle') && !configuration.changed(e, 'defaultTimeFormat') && - !configuration.changed(e, 'integrations', 'enabled') && + !configuration.changed(e, 'integrations.enabled') && !configuration.changed(e, 'sortBranchesBy') ) { return false; @@ -378,19 +378,19 @@ export class RemotesView extends ViewBase { } private setLayout(layout: ViewBranchesLayout) { - return configuration.updateEffective('views', this.configKey, 'branches', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.branches.layout` as const, layout); } private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.files.layout` as const, layout); } private setShowAvatars(enabled: boolean) { - return configuration.updateEffective('views', this.configKey, 'avatars', enabled); + return configuration.updateEffective(`views.${this.configKey}.avatars` as const, enabled); } private async setShowBranchPullRequest(enabled: boolean) { - await configuration.updateEffective('views', this.configKey, 'pullRequests', 'showForBranches', enabled); - await configuration.updateEffective('views', this.configKey, 'pullRequests', 'enabled', enabled); + await configuration.updateEffective(`views.${this.configKey}.pullRequests.showForBranches` as const, enabled); + await configuration.updateEffective(`views.${this.configKey}.pullRequests.enabled` as const, enabled); } } diff --git a/src/views/repositoriesView.ts b/src/views/repositoriesView.ts index 09947f8..09a75c3 100644 --- a/src/views/repositoriesView.ts +++ b/src/views/repositoriesView.ts @@ -250,7 +250,7 @@ export class RepositoriesView extends ViewBase { } private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.files.layout` as const, layout); } } diff --git a/src/views/tagsView.ts b/src/views/tagsView.ts index 32652d2..72a8bb1 100644 --- a/src/views/tagsView.ts +++ b/src/views/tagsView.ts @@ -240,14 +240,14 @@ export class TagsView extends ViewBase { } private setLayout(layout: ViewBranchesLayout) { - return configuration.updateEffective('views', this.configKey, 'branches', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.branches.layout` as const, layout); } private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); + return configuration.updateEffective(`views.${this.configKey}.files.layout` as const, layout); } private setShowAvatars(enabled: boolean) { - return configuration.updateEffective('views', this.configKey, 'avatars', enabled); + return configuration.updateEffective(`views.${this.configKey}.avatars` as const, enabled); } } diff --git a/src/views/viewBase.ts b/src/views/viewBase.ts index 91785bc..a25369f 100644 --- a/src/views/viewBase.ts +++ b/src/views/viewBase.ts @@ -3,12 +3,10 @@ import { CancellationToken, commands, ConfigurationChangeEvent, - ConfigurationTarget, Disposable, Event, EventEmitter, MarkdownString, - MessageItem, TreeDataProvider, TreeItem, TreeItemCollapsibleState, @@ -46,7 +44,7 @@ import { RemotesView } from './remotesView'; import { RepositoriesView } from './repositoriesView'; import { SearchAndCompareView } from './searchAndCompareView'; import { StashesView } from './stashesView'; -import { debug, Functions, log, Promises, Strings } from '../system'; +import { debug, Functions, log, Promises } from '../system'; import { TagsView } from './tagsView'; export type View = @@ -161,9 +159,9 @@ export abstract class ViewBase< protected filterConfigurationChanged(e: ConfigurationChangeEvent) { if (!configuration.changed(e, 'views')) return false; - if (configuration.changed(e, 'views', this.configKey)) return true; + if (configuration.changed(e, `views.${this.configKey}` as const)) return true; for (const key of viewsCommonConfigKeys) { - if (configuration.changed(e, 'views', key)) return true; + if (configuration.changed(e, `views.${key}` as const)) return true; } return false; @@ -502,23 +500,6 @@ export abstract class ViewBase< void (await commands.executeCommand(`${this.id}.focus`)); } catch (ex) { Logger.error(ex); - - const section = Strings.splitSingle(this.id, '.')[1]; - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!configuration.get(section as any, 'enabled')) { - const actions: MessageItem[] = [{ title: 'Enable' }, { title: 'Cancel', isCloseAffordance: true }]; - - const result = await window.showErrorMessage( - `Unable to show the ${this.name} view since it's currently disabled. Would you like to enable it?`, - ...actions, - ); - - if (result === actions[0]) { - await configuration.update(section as any, 'enabled', true, ConfigurationTarget.Global); - - void (await commands.executeCommand(`${this.id}.focus`)); - } - } } } diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index 60e67f6..882f773 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -587,7 +587,7 @@ export class ViewCommands { @debug() private setShowRelativeDateMarkers(enabled: boolean) { - return configuration.updateEffective('views', 'showRelativeDateMarkers', enabled); + return configuration.updateEffective('views.showRelativeDateMarkers', enabled); } @debug() diff --git a/src/webviews/webviewBase.ts b/src/webviews/webviewBase.ts index 109bcf2..1787057 100644 --- a/src/webviews/webviewBase.ts +++ b/src/webviews/webviewBase.ts @@ -212,10 +212,10 @@ export abstract class WebviewBase implements Disposable { let includePullRequest = false; switch (params.key) { - case configuration.name('currentLine', 'format'): + case configuration.name('currentLine.format'): includePullRequest = Container.config.currentLine.pullRequests.enabled; break; - case configuration.name('statusBar', 'format'): + case configuration.name('statusBar.format'): includePullRequest = Container.config.statusBar.pullRequests.enabled; break; }