/// <summary> /// Creates a square image and attaches an event handler to the layout changed event that /// adds the the square in the upper right-hand corner of the TextView via the adornment layer /// </summary> /// <param name="view">The <see cref="IWpfTextView"/> upon which the adornment will be drawn</param> public SaveAllTheTimeAdornment(IWpfTextView view, IVisualStudioOps vsOps) { _view = view; _adornmentLayer = view.GetAdornmentLayer("SaveAllTheTimeAdornment"); var filePath = getFilePathFromView(_view); if (shouldSuppressAdornment(filePath)) { _inner = Disposable.Empty; return; } var commitControl = new CommitHintView() { ViewModel = new CommitHintViewModel(filePath, vsOps, settings), }; var disp = new CompositeDisposable(); var sizeChanged = Observable.Merge( Observable.FromEventPattern <EventHandler, EventArgs>(x => _view.ViewportHeightChanged += x, x => _view.ViewportHeightChanged -= x), Observable.FromEventPattern <EventHandler, EventArgs>(x => _view.ViewportWidthChanged += x, x => _view.ViewportWidthChanged -= x)); var hasAdded = false; disp.Add(sizeChanged.Subscribe(__ => { if (!hasAdded) { _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative, null, null, commitControl, null); hasAdded = true; } // NB: The scheduling is to get around initialization where ActualXXX is zero var action = new Action <double>(_ => { Canvas.SetLeft(commitControl, _view.ViewportRight - commitControl.ActualWidth); Canvas.SetTop(commitControl, _view.ViewportBottom - commitControl.ActualHeight); }); if (commitControl.ActualWidth > 1) { action(commitControl.ActualWidth); } else { commitControl.WhenAny(x => x.ActualWidth, x => x.Value) .Where(x => x > 1) .Take(1) .Subscribe(action); } })); disp.Add(Disposable.Create(() => _adornmentLayer.RemoveAllAdornments())); disp.Add(Observable.FromEventPattern <EventHandler, EventArgs>(x => _view.Closed += x, x => _view.Closed -= x) .Subscribe(_ => Dispose())); _inner = disp; disp.Add(commitControl.ViewModel); }
/// <summary> /// Creates a square image and attaches an event handler to the layout changed event that /// adds the the square in the upper right-hand corner of the TextView via the adornment layer /// </summary> /// <param name="view">The <see cref="IWpfTextView"/> upon which the adornment will be drawn</param> public SaveAllTheTimeAdornment(IWpfTextView view, IVisualStudioOps vsOps) { _view = view; _adornmentLayer = view.GetAdornmentLayer("SaveAllTheTimeAdornment"); var filePath = getFilePathFromView(_view); if (shouldSuppressAdornment(filePath)) { _inner = Disposable.Empty; return; } var commitControl = new CommitHintView() { ViewModel = new CommitHintViewModel(filePath, vsOps, settings), }; var disp = new CompositeDisposable(); var sizeChanged = Observable.Merge( Observable.FromEventPattern<EventHandler, EventArgs>(x => _view.ViewportHeightChanged += x, x => _view.ViewportHeightChanged -= x), Observable.FromEventPattern<EventHandler, EventArgs>(x => _view.ViewportWidthChanged += x, x => _view.ViewportWidthChanged -= x)); var hasAdded = false; disp.Add(sizeChanged.Subscribe(__ => { if (!hasAdded) { _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative, null, null, commitControl, null); hasAdded = true; } // NB: The scheduling is to get around initialization where ActualXXX is zero var action = new Action<double>(_ => { Canvas.SetLeft(commitControl, _view.ViewportRight - commitControl.ActualWidth); Canvas.SetTop(commitControl, _view.ViewportBottom - commitControl.ActualHeight); }); if (commitControl.ActualWidth > 1) { action(commitControl.ActualWidth); } else { commitControl.WhenAny(x => x.ActualWidth, x => x.Value) .Where(x => x > 1) .Take(1) .Subscribe(action); } })); disp.Add(Disposable.Create(() => _adornmentLayer.RemoveAllAdornments())); disp.Add(Observable.FromEventPattern<EventHandler, EventArgs>(x => _view.Closed += x, x => _view.Closed -= x) .Subscribe(_ => Dispose())); _inner = disp; disp.Add(commitControl.ViewModel); }
/// <summary> /// Creates a square image and attaches an event handler to the layout changed event that /// adds the the square in the upper right-hand corner of the TextView via the adornment layer /// </summary> /// <param name="view">The <see cref="IWpfTextView"/> upon which the adornment will be drawn</param> public SaveAllTheTimeAdornment(IWpfTextView view, IVisualStudioOps vsOps) { _view = view; _adornmentLayer = view.GetAdornmentLayer("SaveAllTheTimeAdornment"); var commitControl = new CommitHintView() { ViewModel = new CommitHintViewModel(getFilePathFromView(_view), vsOps, settings), }; var disp = new CompositeDisposable(); var sizeChanged = Observable.Merge( Observable.FromEventPattern<EventHandler, EventArgs>(x => _view.ViewportHeightChanged += x, x => _view.ViewportHeightChanged -= x), Observable.FromEventPattern<EventHandler, EventArgs>(x => _view.ViewportWidthChanged += x, x => _view.ViewportWidthChanged -= x)); var hasAdded = false; disp.Add(sizeChanged.Subscribe(x => { if (!hasAdded) { _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative, null, null, commitControl, null); hasAdded = true; } // NB: The scheduling is to get around initialization where ActualXXX is zero var sched = (commitControl.ActualWidth > 0 ? ImmediateScheduler.Instance : RxApp.MainThreadScheduler); sched.Schedule(() => { Canvas.SetLeft(commitControl, _view.ViewportRight - commitControl.ActualWidth); Canvas.SetTop(commitControl, _view.ViewportBottom - commitControl.ActualHeight); }); })); disp.Add(Disposable.Create(() => _adornmentLayer.RemoveAllAdornments())); disp.Add(Observable.FromEventPattern<EventHandler, EventArgs>(x => _view.Closed += x, x => _view.Closed -= x) .Subscribe(_ => Dispose())); disp.Add(commitControl.ViewModel); _inner = disp; }
public SaveAllTheTimeFactory(IVisualStudioOps vsOps) { _vsOps = vsOps; }
public CommitHintViewModel(string filePath, IVisualStudioOps vsOps, UserSettings settings = null, IGitRepoOps gitRepoOps = null, IFilesystemWatchCache watchCache = null) { FilePath = filePath; watchCache = watchCache ?? _defaultWatchCache; _gitRepoOps = gitRepoOps ?? new GitRepoOps(); UserSettings = settings ?? new UserSettings(); IsGitHubForWindowsInstalled = _gitRepoOps.IsGitHubForWindowsInstalled(); this.Log().Info("Starting Commit Hint for {0}", filePath); this.WhenAny(x => x.FilePath, x => x.Value) .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(_gitRepoOps.FindGitRepo) .ToProperty(this, x => x.RepoPath, out _RepoPath); this.WhenAny(x => x.RepoPath, x => x.Value) .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(_gitRepoOps.ProtocolUrlForRepoPath) .ToProperty(this, x => x.ProtocolUrl, out _ProtocolUrl); Open = new ReactiveCommand(this.WhenAny(x => x.ProtocolUrl, x => !String.IsNullOrWhiteSpace(x.Value))); GoAway = new ReactiveCommand(); RefreshStatus = new ReactiveCommand(this.WhenAny(x => x.RepoPath, x => !String.IsNullOrWhiteSpace(x.Value))); RefreshLastCommitTime = new ReactiveCommand(this.WhenAny(x => x.RepoPath, x => !String.IsNullOrWhiteSpace(x.Value))); ShowTFSGitWarning = new ReactiveCommand(); HelpMe = new ReactiveCommand(); var repoWatchSub = this.WhenAny(x => x.RepoPath, x => x.Value) .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(x => watchCache.Register(Path.Combine(x, ".git", "refs")).Select(_ => x)) .Switch() .InvokeCommand(RefreshLastCommitTime); RefreshLastCommitTime.RegisterAsync(_ => _gitRepoOps.LastCommitTime(RepoPath)) .StartWith(_gitRepoOps.ApplicationStartTime) .ToProperty(this, x => x.LastRepoCommitTime, out _LastRepoCommitTime); MessageBus.Current.Listen <Unit>("AnyDocumentChanged") .Timestamp(RxApp.MainThreadScheduler) .Select(x => x.Timestamp) .StartWith(_gitRepoOps.ApplicationStartTime) .ToProperty(this, x => x.LastTextActiveTime, out _LastTextActiveTime); var refreshDisp = this.WhenAny(x => x.LastTextActiveTime, x => Unit.Default) .Buffer(TimeSpan.FromSeconds(5), RxApp.TaskpoolScheduler) .Where(x => x.Count > 0) .StartWith(new List <Unit> { Unit.Default }) .ObserveOn(RxApp.MainThreadScheduler) .InvokeCommand(RefreshStatus); this.WhenAny(x => x.LastRepoCommitTime, x => x.LastTextActiveTime, x => x.MinutesTimeOverride, (commit, active, _) => active.Value - commit.Value) .Select(x => x.Ticks < 0 ? TimeSpan.Zero : x) .Select(x => MinutesTimeOverride != null ? TimeSpan.FromMinutes(MinutesTimeOverride.Value) : x) .Select(x => LastCommitTimeToOpacity(x)) .ToProperty(this, x => x.SuggestedOpacity, out _SuggestedOpacity, 1.0); var hintState = new Subject <CommitHintState>(); hintState.ToProperty(this, x => x.HintState, out _HintState); Open.Subscribe(_ => vsOps.SaveAll()); RefreshStatus.RegisterAsync(_ => _gitRepoOps.GetStatus(RepoPath)) .ToProperty(this, x => x.LatestRepoStatus, out _LatestRepoStatus); this.WhenAny(x => x.SuggestedOpacity, x => x.LatestRepoStatus, (opacity, status) => new { Opacity = opacity.Value, Status = status.Value }) .Select(x => { if (x.Status == null) { return(CommitHintState.Green); } if (!x.Status.Added.Any() && !x.Status.Removed.Any() && !x.Status.Modified.Any() && !x.Status.Missing.Any()) { return(CommitHintState.Green); } if (x.Opacity >= 0.95) { return(CommitHintState.Red); } if (x.Opacity >= 0.6) { return(CommitHintState.Yellow); } return(CommitHintState.Green); }) .Subscribe(hintState); // NB: Because _LastRepoCommitTime at the end of the day creates a // FileSystemWatcher, we have to dispose it or else we'll get FSW // messages for evar. _inner = new CompositeDisposable(repoWatchSub, _LastRepoCommitTime, _LastTextActiveTime); }
public CommitHintViewModel(string filePath, IVisualStudioOps vsOps, UserSettings settings = null, IGitRepoOps gitRepoOps = null, IFilesystemWatchCache watchCache = null) { FilePath = filePath; watchCache = watchCache ?? _defaultWatchCache; _gitRepoOps = gitRepoOps ?? new GitRepoOps(); UserSettings = settings ?? new UserSettings(); this.Log().Info("Starting Commit Hint for {0}", filePath); this.WhenAny(x => x.FilePath, x => x.Value) .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(_gitRepoOps.FindGitRepo) .ToProperty(this, x => x.RepoPath, out _RepoPath); this.WhenAny(x => x.RepoPath, x => x.Value) .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(_gitRepoOps.ProtocolUrlForRepoPath) .ToProperty(this, x => x.ProtocolUrl, out _ProtocolUrl); Open = new ReactiveCommand(this.WhenAny(x => x.ProtocolUrl, x => !String.IsNullOrWhiteSpace(x.Value))); GoAway = new ReactiveCommand(); RefreshStatus = new ReactiveAsyncCommand(this.WhenAny(x => x.RepoPath, x => !String.IsNullOrWhiteSpace(x.Value))); RefreshLastCommitTime = new ReactiveAsyncCommand(this.WhenAny(x => x.RepoPath, x => !String.IsNullOrWhiteSpace(x.Value))); var repoWatchSub = this.WhenAny(x => x.RepoPath, x => x.Value) .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(x => watchCache.Register(Path.Combine(x, ".git", "refs")).Select(_ => x)) .Switch() .InvokeCommand(RefreshLastCommitTime); RefreshLastCommitTime.RegisterAsyncObservable(_ => _gitRepoOps.LastCommitTime(RepoPath)) .StartWith(_gitRepoOps.ApplicationStartTime) .ToProperty(this, x => x.LastRepoCommitTime, out _LastRepoCommitTime); MessageBus.Current.Listen<Unit>("AnyDocumentChanged") .Timestamp(RxApp.MainThreadScheduler) .Select(x => x.Timestamp) .StartWith(_gitRepoOps.ApplicationStartTime) .ToProperty(this, x => x.LastTextActiveTime, out _LastTextActiveTime); var refreshDisp = this.WhenAny(x => x.LastTextActiveTime, x => Unit.Default) .Buffer(TimeSpan.FromSeconds(5), RxApp.TaskpoolScheduler) .ObserveOn(RxApp.MainThreadScheduler) .InvokeCommand(RefreshStatus); this.WhenAny(x => x.LastRepoCommitTime, x => x.LastTextActiveTime, x => x.MinutesTimeOverride, (commit, active, _) => active.Value - commit.Value) .Select(x => x.Ticks < 0 ? TimeSpan.Zero : x) .Select(x => MinutesTimeOverride != null ? TimeSpan.FromMinutes(MinutesTimeOverride.Value) : x) .Select(x => LastCommitTimeToOpacity(x)) .ToProperty(this, x => x.SuggestedOpacity, out _SuggestedOpacity, 1.0); var hintState = new Subject<CommitHintState>(); hintState.ToProperty(this, x => x.HintState, out _HintState); Open.Subscribe(_ => vsOps.SaveAll()); RefreshStatus.RegisterAsyncObservable(_ => _gitRepoOps.GetStatus(RepoPath)) .ToProperty(this, x => x.LatestRepoStatus, out _LatestRepoStatus); this.WhenAny(x => x.SuggestedOpacity, x => x.LatestRepoStatus, (opacity, status) => new { Opacity = opacity.Value, Status = status.Value }) .Select(x => { if (x.Status == null) return CommitHintState.Green; if (!x.Status.Added.Any() && !x.Status.Removed.Any() && !x.Status.Modified.Any() && !x.Status.Missing.Any()) return CommitHintState.Green; if (x.Opacity >= 0.95) return CommitHintState.Red; if (x.Opacity >= 0.6) return CommitHintState.Yellow; return CommitHintState.Green; }) .Subscribe(hintState); // NB: Because _LastRepoCommitTime at the end of the day creates a // FileSystemWatcher, we have to dispose it or else we'll get FSW // messages for evar. _inner = new CompositeDisposable(repoWatchSub, _LastRepoCommitTime, _LastTextActiveTime); }