Path: blob/main/extensions/markdown-language-features/src/client/fileWatchingManager.ts
3292 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { Utils } from 'vscode-uri';7import { disposeAll, IDisposable } from '../util/dispose';8import { ResourceMap } from '../util/resourceMap';9import { Schemes } from '../util/schemes';1011type DirWatcherEntry = {12readonly uri: vscode.Uri;13readonly disposables: readonly IDisposable[];14};151617export class FileWatcherManager {1819private readonly _fileWatchers = new Map<number, {20readonly watcher: vscode.FileSystemWatcher;21readonly dirWatchers: DirWatcherEntry[];22}>();2324private readonly _dirWatchers = new ResourceMap<{25readonly watcher: vscode.FileSystemWatcher;26refCount: number;27}>();2829create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void {30// Non-writable file systems do not support file watching31if (!vscode.workspace.fs.isWritableFileSystem(uri.scheme)) {32return;33}3435const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete);36const parentDirWatchers: DirWatcherEntry[] = [];37this._fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers });3839if (listeners.create) { watcher.onDidCreate(listeners.create); }40if (listeners.change) { watcher.onDidChange(listeners.change); }41if (listeners.delete) { watcher.onDidDelete(listeners.delete); }4243if (watchParentDirs && uri.scheme !== Schemes.untitled) {44// We need to watch the parent directories too for when these are deleted / created45for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) {46const disposables: IDisposable[] = [];4748let parentDirWatcher = this._dirWatchers.get(dirUri);49if (!parentDirWatcher) {50const glob = new vscode.RelativePattern(Utils.dirname(dirUri), Utils.basename(dirUri));51const parentWatcher = vscode.workspace.createFileSystemWatcher(glob, !listeners.create, true, !listeners.delete);52parentDirWatcher = { refCount: 0, watcher: parentWatcher };53this._dirWatchers.set(dirUri, parentDirWatcher);54}55parentDirWatcher.refCount++;5657if (listeners.create) {58disposables.push(parentDirWatcher.watcher.onDidCreate(async () => {59// Just because the parent dir was created doesn't mean our file was created60try {61const stat = await vscode.workspace.fs.stat(uri);62if (stat.type === vscode.FileType.File) {63listeners.create!();64}65} catch {66// Noop67}68}));69}7071if (listeners.delete) {72// When the parent dir is deleted, consider our file deleted too73// TODO: this fires if the file previously did not exist and then the parent is deleted74disposables.push(parentDirWatcher.watcher.onDidDelete(listeners.delete));75}7677parentDirWatchers.push({ uri: dirUri, disposables });78}79}80}8182delete(id: number): void {83const entry = this._fileWatchers.get(id);84if (entry) {85for (const dirWatcher of entry.dirWatchers) {86disposeAll(dirWatcher.disposables);8788const dirWatcherEntry = this._dirWatchers.get(dirWatcher.uri);89if (dirWatcherEntry) {90if (--dirWatcherEntry.refCount <= 0) {91dirWatcherEntry.watcher.dispose();92this._dirWatchers.delete(dirWatcher.uri);93}94}95}9697entry.watcher.dispose();98}99100this._fileWatchers.delete(id);101}102}103104105