A service that wraps the Visual Studio file watching APIs to make them more convenient for use. With this, a consumer can create an IContext which lets you add/remove files being watched, and an event is raised when a file is modified.
            public void InitializeFileTracking(FileChangeWatcher fileChangeWatcher)
            {
                lock (_gate)
                {
                    if (_fileChangeContext == null)
                    {
                        ImmutableArray <string> includes;

                        try
                        {
                            includes = RuleSet.GetEffectiveIncludesFromFile(FilePath);
                        }
                        catch (Exception e)
                        {
                            // We couldn't read the rule set for whatever reason. Capture the exception
                            // so we can surface the error later, and subscribe to file change notifications
                            // so that we'll automatically reload the file if the user can fix the issue.
                            _optionsRead = true;
                            _specificDiagnosticOptions = ImmutableDictionary <string, ReportDiagnostic> .Empty;
                            _exception = e;

                            includes = ImmutableArray.Create(FilePath);
                        }

                        _fileChangeContext              = fileChangeWatcher.CreateContext();
                        _fileChangeContext.FileChanged += IncludeUpdated;

                        foreach (var include in includes)
                        {
                            _fileChangeContext.EnqueueWatchingFile(include);
                        }
                    }
                }
            }
 public VisualStudioRuleSetManager(
     FileChangeWatcher fileChangeWatcher, IForegroundNotificationService foregroundNotificationService, IAsynchronousOperationListener listener)
 {
     _fileChangeWatcher             = fileChangeWatcher;
     _foregroundNotificationService = foregroundNotificationService;
     _listener = listener;
 }
        public FileChangeWatcherProvider(IThreadingContext threadingContext, [Import(typeof(SVsServiceProvider))] Shell.IAsyncServiceProvider serviceProvider)
        {
            // We do not want background work to implicitly block on the availability of the SVsFileChangeEx to avoid any deadlock risk,
            // since the first fetch for a file watcher might end up happening on the background.
            Watcher = new FileChangeWatcher(_fileChangeService.Task);

            System.Threading.Tasks.Task.Run(async() =>
            {
                await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();

                var fileChangeService = (IVsAsyncFileChangeEx)await serviceProvider.GetServiceAsync(typeof(SVsFileChangeEx)).ConfigureAwait(true);
                _fileChangeService.SetResult(fileChangeService);
            });
        }
            public Context(FileChangeWatcher fileChangeWatcher, string directoryFilePath)
            {
                _fileChangeWatcher     = fileChangeWatcher;
                _noOpFileWatchingToken = new FileWatchingToken();

                if (directoryFilePath != null)
                {
                    if (!directoryFilePath.EndsWith("\\"))
                    {
                        directoryFilePath = directoryFilePath + "\\";
                    }

                    _directoryFilePathOpt = directoryFilePath;

                    _fileChangeWatcher.EnqueueWork(
                        service => { ErrorHandler.ThrowOnFailure(service.AdviseDirChange(_directoryFilePathOpt, fWatchSubDir: 1, this, out _directoryWatchCookie)); });
                }
            }
            public Context(FileChangeWatcher fileChangeWatcher, string?directoryFilePath)
            {
                _fileChangeWatcher     = fileChangeWatcher;
                _noOpFileWatchingToken = new FileWatchingToken();

                if (directoryFilePath != null)
                {
                    if (!directoryFilePath.EndsWith("\\"))
                    {
                        directoryFilePath += "\\";
                    }

                    _directoryFilePath = directoryFilePath;

                    _fileChangeWatcher.EnqueueWork(
                        async service =>
                    {
                        _directoryWatchCookie = await service.AdviseDirChangeAsync(_directoryFilePath, watchSubdirectories: true, this).ConfigureAwait(false);
                    });
                }
            }
        public FileChangeWatcherProvider(
            IThreadingContext threadingContext,
            IAsynchronousOperationListenerProvider listenerProvider,
            [Import(typeof(SVsServiceProvider))] Shell.IAsyncServiceProvider serviceProvider)
        {
            var fileChangeService = Task.Factory.StartNew(
                async() =>
            {
                await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(threadingContext.DisposalToken);

                var fileChangeService = (IVsAsyncFileChangeEx?)await serviceProvider.GetServiceAsync(typeof(SVsFileChangeEx)).ConfigureAwait(true);
                Assumes.Present(fileChangeService);
                return(fileChangeService);
            },
                threadingContext.DisposalToken,
                TaskCreationOptions.RunContinuationsAsynchronously,
                TaskScheduler.Default)
                                    .Unwrap();

            // We do not want background work to implicitly block on the availability of the SVsFileChangeEx to avoid any deadlock risk,
            // since the first fetch for a file watcher might end up happening on the background.
            Watcher = new FileChangeWatcher(listenerProvider, fileChangeService);
        }