Skip to content

Commit c4cf967

Browse files
committed
feat(vscode): add Reload Vault command to refresh files.exclude patterns
New dotfiles or directories added to the vault root outside VS Code are not picked up until an unrelated config change triggers syncFilesExclude. "Obsidian VFS: Reload Vault" provides a manual Command Palette trigger that re-scans the vault root, recomputes both exclude tiers, and refreshes the Explorer tree view. Assisted-by: Claude
1 parent 393141f commit c4cf967

6 files changed

Lines changed: 64 additions & 3 deletions

File tree

packages/vscode/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Available via the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`):
3838
| `Obsidian VFS: Copy Path` | Copy the active file's `obs://` URI to the clipboard (`Shift+Alt+Cmd+C` on `obs://` files) |
3939
| `Obsidian VFS: Move into Vault` | Move a file from the current project into a mounted vault folder (Explorer context menu, editor title, or Command Palette) |
4040
| `Obsidian VFS: Duplicate into Vault` | Duplicate a file from the current project into a mounted vault folder (Explorer context menu, editor title, or Command Palette) |
41+
| `Obsidian VFS: Reload Vault` | Re-scan the vault root and refresh `files.exclude` patterns and Explorer tree view |
4142

4243
## Settings
4344

@@ -107,6 +108,7 @@ Non-autoMount vault content (`.obsidian/`, `.trash/`, and any directories not in
107108

108109
- The extension scans the vault root and adds `files.exclude` patterns for entries not in `autoMount`. Patterns are split into two tiers: vault-global patterns (dotfiles and `blocked` paths) are written to `<vault>/.vscode/settings.json` — controlled by `vault.excludeDotfiles`, `vault.excludeDotfilePattern`, and `vault.excludeBlocked`; remaining non-autoMount directories and file extension globs are written to workspace settings — controlled by `workspace.excludeUnmountedFolders`, `workspace.excludeUnmountedFiles`, and `workspace.excludeUnmountedFilePattern`. All managed patterns are tracked internally for cleanup.
109110
- When `autoMount` entries or any `vault.*`/`workspace.*` toggle changes, patterns are re-synced automatically — stale patterns are removed and new ones added.
111+
- When new dotfiles or directories are added to the vault outside VS Code, run **Obsidian VFS: Reload Vault** to manually refresh patterns and the tree view.
110112
- When `obsidianVFS.workspace.enabled` is disabled, all managed patterns are removed and the workspace folder is deleted.
111113
- The vault's `.git` repository is automatically added to `git.ignoredRepositories` (user-level setting) when `vault.gitIgnore` is true and a workspace folder is active, preventing VS Code's Git extension from listing it in Source Control. The entry is removed when `vault.gitIgnore` is disabled or the workspace folder is removed.
112114

packages/vscode/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "obsidian-vfs",
33
"displayName": "Obsidian VFS",
44
"description": "Browse, search, and edit your Obsidian vault directly in VSCode via a virtual file system (obs://)",
5-
"version": "1.2.1",
5+
"version": "1.2.2",
66
"private": true,
77
"publisher": "otaviof",
88
"engines": {
@@ -63,6 +63,10 @@
6363
{
6464
"command": "obsidianVFS.duplicateIntoVault",
6565
"title": "Obsidian VFS: Duplicate into Vault"
66+
},
67+
{
68+
"command": "obsidianVFS.reloadVault",
69+
"title": "Obsidian VFS: Reload Vault"
6670
}
6771
],
6872
"keybindings": [

packages/vscode/src/commands.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ function invokeCommandWithUri(
9494
return { handler, tree, channel };
9595
}
9696

97+
describe("COMMAND constant", () => {
98+
it("defines reloadVault command identifier", () => {
99+
expect(COMMAND.reloadVault).toBe("obsidianVFS.reloadVault");
100+
});
101+
});
102+
97103
describe("registerCommands", () => {
98104
beforeEach(() => {
99105
vi.clearAllMocks();

packages/vscode/src/extension.test.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ import type { LocalIndexTracker } from "@obsidian-vfs/core";
8383
import { bootstrapFromConfig, readConfig } from "./bootstrap.js";
8484
import { registerCommands } from "./commands.js";
8585
import { activate, deactivate } from "./extension.js";
86-
import { CONFIG_KEY } from "./types.js";
86+
import { COMMAND, CONFIG_KEY } from "./types.js";
8787
import { SCHEME } from "./uri-adapter.js";
8888
import { FOLDER_NAME_PREFIX } from "./workspace-folder.js";
8989
import { ObsidianFileSystemProvider } from "./file-system-provider.js";
@@ -471,6 +471,47 @@ describe("activate", () => {
471471
{ isCaseSensitive: true, isReadonly: false },
472472
);
473473
});
474+
475+
it("registers reloadVault command", async () => {
476+
bootstrapOk();
477+
mockReadConfig.mockReturnValueOnce(fakeExtensionConfig({ workspaceEnabled: false }));
478+
479+
const ctx = fakeContext();
480+
await activate(ctx as never);
481+
482+
const reloadVaultCall = vi
483+
.mocked(vscode.commands.registerCommand)
484+
.mock.calls.find((call) => call[0] === COMMAND.reloadVault);
485+
486+
expect(reloadVaultCall).toBeDefined();
487+
expect(reloadVaultCall![1]).toBeTypeOf("function");
488+
});
489+
490+
it("reloadVault command triggers tree refresh", async () => {
491+
bootstrapOk();
492+
mockReadConfig.mockReturnValueOnce(fakeExtensionConfig({ workspaceEnabled: false }));
493+
mockHasWF.mockReturnValue(true);
494+
495+
const ctx = fakeContext();
496+
await activate(ctx as never);
497+
498+
const treeProviderInstance = vi.mocked(VaultTreeDataProvider).mock.results[0].value as {
499+
refresh: ReturnType<typeof vi.fn>;
500+
};
501+
502+
treeProviderInstance.refresh.mockClear();
503+
504+
const reloadVaultCall = vi
505+
.mocked(vscode.commands.registerCommand)
506+
.mock.calls.find((call) => call[0] === COMMAND.reloadVault);
507+
508+
expect(reloadVaultCall).toBeDefined();
509+
510+
const reloadHandler = reloadVaultCall![1] as () => void;
511+
reloadHandler();
512+
513+
expect(treeProviderInstance.refresh).toHaveBeenCalled();
514+
});
474515
});
475516

476517
describe("migrateStrandedAutoMount", () => {

packages/vscode/src/extension.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { parse as parseJsonc } from "jsonc-parser";
55
import { VAULT_MODE } from "@obsidian-vfs/core";
66

77
import { bootstrapFromConfig, readConfig } from "./bootstrap.js";
8-
import { CONFIG_KEY, CONFIG_PROP, CONFIG_SECTION } from "./types.js";
8+
import { COMMAND, CONFIG_KEY, CONFIG_PROP, CONFIG_SECTION } from "./types.js";
99
import type { ExtensionConfig } from "./types.js";
1010
import { SCHEME } from "./uri-adapter.js";
1111
import { registerCommands } from "./commands.js";
@@ -425,6 +425,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
425425

426426
await activateWorkspaceFolder(wsCtx, config);
427427

428+
context.subscriptions.push(
429+
vscode.commands.registerCommand(COMMAND.reloadVault, () => {
430+
scheduleSync(wsCtx, readConfig());
431+
treeProvider.refresh();
432+
}),
433+
);
434+
428435
context.subscriptions.push(
429436
vscode.workspace.onDidChangeConfiguration((e) =>
430437
handleConfigChange(e, wsCtx, treeProvider, statusBar, provider, providerReg),

packages/vscode/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const COMMAND = {
5252
copyPath: `${CONFIG_SECTION}.copyPath`,
5353
moveIntoVault: `${CONFIG_SECTION}.moveIntoVault`,
5454
duplicateIntoVault: `${CONFIG_SECTION}.duplicateIntoVault`,
55+
reloadVault: `${CONFIG_SECTION}.reloadVault`,
5556
} as const;
5657

5758
import type { VaultMode } from "@obsidian-vfs/core";

0 commit comments

Comments
 (0)