Пример #1
0
        /// <summary>
        /// Executes <paramref name="updateFunc"/> on the current snapshot within a lock.
        /// If a different snapshot object is returned, <see cref="CurrentSnapshot"/> is updated
        /// and an invocation of <see cref="SnapshotChanged"/> is scheduled.
        /// </summary>
        private void TryUpdateSnapshot(Func <DependenciesSnapshot, DependenciesSnapshot> updateFunc, CancellationToken token = default)
        {
            lock (_snapshotLock)
            {
                DependenciesSnapshot updatedSnapshot = updateFunc(_currentSnapshot);

                if (ReferenceEquals(_currentSnapshot, updatedSnapshot))
                {
                    return;
                }

                _currentSnapshot = updatedSnapshot;
            }

            // avoid unnecessary tree updates
            _dependenciesUpdateScheduler.ScheduleAsyncTask(
                ct =>
            {
                if (ct.IsCancellationRequested || IsDisposing || IsDisposed)
                {
                    return(Task.FromCanceled(ct));
                }

                IDependenciesSnapshot snapshot = _currentSnapshot;

                if (snapshot != null)
                {
                    SnapshotChanged?.Invoke(this, new SnapshotChangedEventArgs(snapshot, ct));
                }

                return(Task.CompletedTask);
            }, token);
        }
        private void TryUpdateSnapshot(Func <DependenciesSnapshot, DependenciesSnapshot> updateFunc, CancellationToken token = default)
        {
            lock (_snapshotLock)
            {
                DependenciesSnapshot updatedSnapshot = updateFunc(_currentSnapshot);

                if (ReferenceEquals(_currentSnapshot, updatedSnapshot))
                {
                    return;
                }

                _currentSnapshot = updatedSnapshot;
            }

            // Conflate rapid snapshot updates by debouncing events over a short window.
            // This reduces the frequency of tree updates with minimal perceived latency.
            _dependenciesUpdateScheduler.ScheduleAsyncTask(
                ct =>
            {
                if (ct.IsCancellationRequested || IsDisposing || IsDisposed)
                {
                    return(Task.FromCanceled(ct));
                }

                // Always publish the latest snapshot
                IDependenciesSnapshot snapshot = _currentSnapshot;

                var events = new SnapshotChangedEventArgs(snapshot, ct);
                _snapshotChangedSource.Post(events);
                SnapshotChanged?.Invoke(this, events);

                return(Task.CompletedTask);
            }, token);
        }
        public void RegisterSnapshotProvider(IDependenciesSnapshotProvider snapshotProvider)
        {
            if (snapshotProvider == null)
            {
                return;
            }

            lock (_snapshotProviders)
            {
                _snapshotProviders[snapshotProvider.CurrentSnapshot.ProjectPath] = snapshotProvider;
                snapshotProvider.SnapshotRenamed           += OnSnapshotRenamed;
                snapshotProvider.SnapshotChanged           += OnSnapshotChanged;
                snapshotProvider.SnapshotProviderUnloading += OnSnapshotProviderUnloading;
            }

            void OnSnapshotProviderUnloading(object sender, SnapshotProviderUnloadingEventArgs e)
            {
                // Project has unloaded, so remove it from the cache and unregister event handlers
                SnapshotProviderUnloading?.Invoke(this, e);

                lock (_snapshotProviders)
                {
                    _snapshotProviders.Remove(snapshotProvider.CurrentSnapshot.ProjectPath);
                    snapshotProvider.SnapshotRenamed           -= OnSnapshotRenamed;
                    snapshotProvider.SnapshotChanged           -= OnSnapshotChanged;
                    snapshotProvider.SnapshotProviderUnloading -= OnSnapshotProviderUnloading;
                }
            }

            void OnSnapshotRenamed(object sender, ProjectRenamedEventArgs e)
            {
                lock (_snapshotProviders)
                {
                    // Remove and re-add provider with new project path
                    if (!string.IsNullOrEmpty(e.OldFullPath) &&
                        _snapshotProviders.TryGetValue(e.OldFullPath, out IDependenciesSnapshotProvider provider) &&
                        _snapshotProviders.Remove(e.OldFullPath) &&
                        provider != null &&
                        !string.IsNullOrEmpty(e.NewFullPath))
                    {
                        _snapshotProviders[e.NewFullPath] = provider;
                    }
                }
            }

            void OnSnapshotChanged(object sender, SnapshotChangedEventArgs e)
            {
                // Propagate the change event
                SnapshotChanged?.Invoke(this, e);
            }
        }
        private void ScheduleDependenciesUpdate(CancellationToken token = default)
        {
            _dependenciesUpdateScheduler.ScheduleAsyncTask(ct =>
            {
                if (ct.IsCancellationRequested || IsDisposing || IsDisposed)
                {
                    return(Task.FromCanceled(ct));
                }

                IDependenciesSnapshot snapshot = _currentSnapshot;

                if (snapshot != null)
                {
                    SnapshotChanged?.Invoke(this, new SnapshotChangedEventArgs(snapshot, ct));
                }

                return(Task.CompletedTask);
            }, token);
        }
Пример #5
0
 private void OnSnapshotChanged(object sender, SnapshotChangedEventArgs e)
 {
     SnapshotChanged?.Invoke(this, e);
 }
Пример #6
0
 private void OnDependenciesSnapshotChanged(IDependenciesSnapshot snapshot)
 {
     SnapshotChanged?.Invoke(this, new SnapshotChangedEventArgs(snapshot));
 }
Пример #7
0
        /// <inheritdoc />
        public IDisposable RegisterSnapshotProvider(IDependenciesSnapshotProvider snapshotProvider)
        {
            Requires.NotNull(snapshotProvider, nameof(snapshotProvider));

            var unregister = new DisposableBag();

            lock (_lock)
            {
                snapshotProvider.SnapshotRenamed           += OnSnapshotRenamed;
                snapshotProvider.SnapshotProviderUnloading += OnSnapshotProviderUnloading;

                ITargetBlock <SnapshotChangedEventArgs> actionBlock = DataflowBlockSlim.CreateActionBlock <SnapshotChangedEventArgs>(
                    e => SnapshotChanged?.Invoke(this, e),
                    "AggregateDependenciesSnapshotProviderSource {1}",
                    skipIntermediateInputData: true);

                unregister.Add(
                    snapshotProvider.SnapshotChangedSource.LinkTo(
                        actionBlock,
                        DataflowOption.PropagateCompletion));

                _snapshotProviderByPath = _snapshotProviderByPath.SetItem(snapshotProvider.CurrentSnapshot.ProjectPath, snapshotProvider);
            }

            unregister.Add(new DisposableDelegate(
                               () =>
            {
                lock (_lock)
                {
                    string projectPath                          = snapshotProvider.CurrentSnapshot.ProjectPath;
                    _snapshotProviderByPath                     = _snapshotProviderByPath.Remove(projectPath);
                    snapshotProvider.SnapshotRenamed           -= OnSnapshotRenamed;
                    snapshotProvider.SnapshotProviderUnloading -= OnSnapshotProviderUnloading;
                }
            }));

            return(unregister);

            void OnSnapshotProviderUnloading(object sender, SnapshotProviderUnloadingEventArgs e)
            {
                // Project has unloaded, so remove it from the cache and unregister event handlers
                SnapshotProviderUnloading?.Invoke(this, e);

                unregister.Dispose();
            }

            void OnSnapshotRenamed(object sender, ProjectRenamedEventArgs e)
            {
                if (string.IsNullOrEmpty(e.OldFullPath))
                {
                    return;
                }

                lock (_lock)
                {
                    // Remove and re-add provider with new project path
                    if (_snapshotProviderByPath.TryGetValue(e.OldFullPath, out IDependenciesSnapshotProvider provider))
                    {
                        _snapshotProviderByPath = _snapshotProviderByPath.Remove(e.OldFullPath);

                        if (!string.IsNullOrEmpty(e.NewFullPath))
                        {
                            _snapshotProviderByPath = _snapshotProviderByPath.SetItem(e.NewFullPath, provider);
                        }
                    }
                }
            }
        }