diff --git a/README.md b/README.md index e88ccc1..159c27e 100644 --- a/README.md +++ b/README.md @@ -22,19 +22,21 @@ Quickly toggles excluded (hidden) files visibility in the file explorer. - Adds a `Toggle Excluded Files` command (`toggleexcludedfiles.toggle`) with a shortcut of `ctrl+shift+a` (`cmd+shift+a` on macOS) to either show or restore the current visibility of excluded files in the file explorer +- Adds a **Explorer view button** to toggle the excluded file visibility ([optional](#extension-settings), on by default) - Adds a **status bar button** to toggle the excluded file visibility ([optional](#extension-settings), on by default) - An indicator icon will show when the exclude visibility is currently toggled - Adds a `Show Excluded Files` command (`toggleexcludedfiles.show`) to show excluded files in the file explorer -- Adds a `Restore Excluded Files` command (`toggleexcludedfiles.restore`) to restore (hide) excluded files in the file explorer +- Adds a `Hide Excluded Files` command (`toggleexcludedfiles.restore`) to hide (restore) excluded files in the file explorer ## Extension Settings -| Name | Description | -| --------------------------------------- | ------------------------------------------------------------- | -| `toggleexcludedfiles.statusBar.enabled` | Specifies whether to show the toggle button in the status bar | +| Name | Description | +| --------------------------------------- | ---------------------------------------------------------------- | +| `toggleexcludedfiles.explorer.enabled` | Specifies whether to show the toggle button in the Explorer view | +| `toggleexcludedfiles.statusBar.enabled` | Specifies whether to show the toggle button in the status bar | ## Known Issues diff --git a/package.json b/package.json index b547879..189ad0a 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,12 @@ "type": "object", "title": "Toggle Excluded Files configuration", "properties": { + "toggleexcludedfiles.explorer.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "Specifies whether to show the toggle button in the Explorer view", + "scope": "window" + }, "toggleexcludedfiles.statusBar.enabled": { "type": "boolean", "default": true, @@ -89,12 +95,14 @@ { "command": "toggleexcludedfiles.show", "title": "Show Excluded Files", - "category": "Files" + "category": "Files", + "icon": "$(eye)" }, { "command": "toggleexcludedfiles.restore", - "title": "Restore Excluded Files", - "category": "Files" + "title": "Hide Excluded Files", + "category": "Files", + "icon": "$(eye-closed)" }, { "command": "toggleexcludedfiles.toggle", @@ -109,7 +117,31 @@ "mac": "cmd+shift+a", "when": "filesExplorerFocus" } - ] + ], + "menus": { + "commandPalette": [ + { + "command": "toggleexcludedfiles.show", + "when": "toggleexcludedfiles:loaded && !toggleexcludedfiles:toggled" + }, + { + "command": "toggleexcludedfiles.restore", + "when": "toggleexcludedfiles:loaded && toggleexcludedfiles:toggled" + } + ], + "view/title": [ + { + "command": "toggleexcludedfiles.show", + "group": "navigation@21", + "when": "view == 'workbench.explorer.fileView' && toggleexcludedfiles:loaded && !toggleexcludedfiles:toggled && config.toggleexcludedfiles.explorer.enabled" + }, + { + "command": "toggleexcludedfiles.restore", + "group": "navigation@21", + "when": "view == 'workbench.explorer.fileView' && toggleexcludedfiles:loaded && toggleexcludedfiles:toggled && config.toggleexcludedfiles.explorer.enabled" + } + ] + } }, "scripts": { "analyze:bundle": "webpack --mode production --env analyzeBundle", diff --git a/src/config.ts b/src/config.ts index 2c9aa5c..475fedd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,13 +7,14 @@ export enum OutputLevel { Debug = 'debug', } -export interface StatusBarConfig { - enabled: boolean; -} - export interface Config { + explorer: { + enabled: boolean; + }; outputLevel: OutputLevel; - statusBar: StatusBarConfig; + statusBar: { + enabled: boolean; + }; } export function fromOutputLevel(level: LogLevel | OutputLevel): LogLevel { diff --git a/src/constants.ts b/src/constants.ts index 9c3226c..12caa95 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,10 @@ export const commandPrefix = 'toggleexcludedfiles'; export const configPrefix = 'toggleexcludedfiles'; -export const extensionId = 'toggleexcludedfiles'; +export const enum ContextKeys { + Loaded = 'toggleexcludedfiles:loaded', + Toggled = 'toggleexcludedfiles:toggled', +} export enum CoreCommands { Open = 'vscode.open', diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..83aa12e --- /dev/null +++ b/src/context.ts @@ -0,0 +1,22 @@ +import { commands, EventEmitter } from 'vscode'; +import type { ContextKeys } from './constants'; +import { CoreCommands } from './constants'; + +const contextStorage = new Map(); + +type AllContextKeys = ContextKeys; + +const _onDidChangeContext = new EventEmitter(); +export const onDidChangeContext = _onDidChangeContext.event; + +export function getContext(key: AllContextKeys): T | undefined; +export function getContext(key: AllContextKeys, defaultValue: T): T; +export function getContext(key: AllContextKeys, defaultValue?: T): T | undefined { + return (contextStorage.get(key) as T | undefined) ?? defaultValue; +} + +export async function setContext(key: AllContextKeys, value: unknown): Promise { + contextStorage.set(key, value); + void (await commands.executeCommand(CoreCommands.SetContext, key, value)); + _onDidChangeContext.fire(key); +} diff --git a/src/excludeController.ts b/src/excludeController.ts index 693b5ef..a8898a9 100644 --- a/src/excludeController.ts +++ b/src/excludeController.ts @@ -1,7 +1,8 @@ -import type { Event } from 'vscode'; -import { ConfigurationTarget, Disposable, EventEmitter, workspace } from 'vscode'; -import { WorkspaceState } from './constants'; +import type { ConfigurationChangeEvent, Disposable, Event } from 'vscode'; +import { ConfigurationTarget, EventEmitter } from 'vscode'; +import { ContextKeys, WorkspaceState } from './constants'; import type { Container } from './container'; +import { setContext } from './context'; import { configuration } from './system/configuration'; import { Logger } from './system/logger'; import { areEqual } from './system/object'; @@ -20,13 +21,8 @@ export class FilesExcludeController implements Disposable { private _working: boolean = false; constructor(private readonly container: Container) { + this._disposable = configuration.onDidChangeAny(this.onConfigurationChanged, this); this.onConfigurationChanged(); - - const subscriptions: Disposable[] = []; - - subscriptions.push(configuration.onDidChangeAny(this.onConfigurationChanged, this)); - - this._disposable = Disposable.from(...subscriptions); } dispose() { @@ -41,17 +37,18 @@ export class FilesExcludeController implements Disposable { return WorkspaceState.SavedState; } - private onConfigurationChanged() { + private onConfigurationChanged(e?: ConfigurationChangeEvent) { if (this._working) return; + if (e != null && !e.affectsConfiguration(this._section)) return; const savedExclude = this.getSavedExcludeConfiguration(); - if (savedExclude === undefined) return; + if (savedExclude == null) return; Logger.log('ExcludeController.onConfigurationChanged'); const newExclude = this.getExcludeConfiguration(); if ( - newExclude !== undefined && + newExclude != null && areEqual(savedExclude.globalValue, newExclude.globalValue) && areEqual(savedExclude.workspaceValue, newExclude.workspaceValue) ) { @@ -60,8 +57,8 @@ export class FilesExcludeController implements Disposable { const appliedExclude = this.getAppliedExcludeConfiguration(); if ( - newExclude !== undefined && - appliedExclude !== undefined && + newExclude != null && + appliedExclude != null && areEqual(appliedExclude.globalValue, newExclude.globalValue) && areEqual(appliedExclude.workspaceValue, newExclude.workspaceValue) ) { @@ -150,13 +147,19 @@ export class FilesExcludeController implements Disposable { const promises: Thenable[] = []; - if (savedExclude !== undefined) { - if (savedExclude.globalValue !== undefined) { - promises.push(workspace.getConfiguration().update(this._section, savedExclude.globalValue, true)); + if (savedExclude != null) { + if (savedExclude.globalValue != null) { + promises.push( + configuration.updateAny(this._section, savedExclude.globalValue, ConfigurationTarget.Global), + ); } - if (savedExclude.workspaceValue !== undefined) { + if (savedExclude.workspaceValue != null) { promises.push( - workspace.getConfiguration().update(this._section, savedExclude.workspaceValue, false), + configuration.updateAny( + this._section, + savedExclude.workspaceValue, + ConfigurationTarget.Workspace, + ), ); } } @@ -212,8 +215,15 @@ export class FilesExcludeController implements Disposable { return this.container.context.workspaceState.get(this.appliedState); } + private _loaded = false; private getSavedExcludeConfiguration(): FilesExcludeInspect | undefined { - return this.container.context.workspaceState.get(this.savedState); + const state = this.container.context.workspaceState.get(this.savedState); + void setContext(ContextKeys.Toggled, state != null); + if (!this._loaded) { + this._loaded = true; + void setContext(ContextKeys.Loaded, true); + } + return state; } private hasSavedExcludeConfiguration(): boolean { diff --git a/src/statusBarController.ts b/src/statusBarController.ts index cf81709..c9c4a3d 100644 --- a/src/statusBarController.ts +++ b/src/statusBarController.ts @@ -27,7 +27,7 @@ export class StatusBarController implements Disposable { if (e == null || configuration.changed(e, 'statusBar.enabled') || e.affectsConfiguration('files.exclude')) { this._statusBarItem?.dispose(); - const canToggle = this.container.filesExclude.canToggle; + const { canToggle } = this.container.filesExclude; if (configuration.get('statusBar.enabled') && canToggle) { this._statusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 0); this._statusBarItem.command = `${commandPrefix}.toggle`; @@ -41,7 +41,7 @@ export class StatusBarController implements Disposable { if (this._statusBarItem == null) return; this._statusBarItem.text = toggled ? '$(eye-closed)' : '$(eye)'; - this._statusBarItem.tooltip = `${toggled ? 'Restore' : 'Show'} Excluded Files`; + this._statusBarItem.tooltip = `${toggled ? 'Hide' : 'Show'} Excluded Files`; } private _onExcludeToggled() {