Skip to content

Commit a4c1c06

Browse files
committed
Issue #423
1 parent 31af1cd commit a4c1c06

File tree

6 files changed

+156
-5
lines changed

6 files changed

+156
-5
lines changed

RetailCoder.VBE/App.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ async void sink_ComponentRenamed(object sender, DispatcherRenamedEventArgs<VBCom
311311
return;
312312
}
313313

314+
_panelVM.RenameComponent(e.Item, e.OldName);
315+
314316
_logger.Debug("Component '{0}' was renamed to '{1}'.", e.OldName, e.Item.Name);
315317

316318
_parser.State.RemoveRenamedComponent(e.Item, e.OldName);

RetailCoder.VBE/UI/SourceControl/SourceControlViewViewModel.cs

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
using System.Collections.ObjectModel;
44
using System.Drawing;
55
using System.Globalization;
6+
using System.IO;
67
using System.Linq;
78
using System.Windows.Forms;
89
using System.Windows.Input;
910
using System.Windows.Media.Imaging;
1011
using Microsoft.Vbe.Interop;
1112
using Ninject;
13+
using NLog;
1214
using Rubberduck.Parsing.VBA;
1315
using Rubberduck.SourceControl;
1416
using Rubberduck.UI.Command;
@@ -35,6 +37,9 @@ public sealed class SourceControlViewViewModel : ViewModelBase, IDisposable
3537
private readonly ISourceControlConfigProvider _configService;
3638
private readonly SourceControlSettings _config;
3739
private readonly ICodePaneWrapperFactory _wrapperFactory;
40+
private readonly IMessageBox _messageBox;
41+
private readonly FileSystemWatcher _fileSystemWatcher;
42+
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
3843

3944
public SourceControlViewViewModel(
4045
VBE vbe,
@@ -46,7 +51,8 @@ public SourceControlViewViewModel(
4651
[Named("branchesView")] IControlView branchesView,
4752
[Named("unsyncedCommitsView")] IControlView unsyncedCommitsView,
4853
[Named("settingsView")] IControlView settingsView,
49-
ICodePaneWrapperFactory wrapperFactory)
54+
ICodePaneWrapperFactory wrapperFactory,
55+
IMessageBox messageBox)
5056
{
5157
_vbe = vbe;
5258
_state = state;
@@ -58,6 +64,7 @@ public SourceControlViewViewModel(
5864
_configService = configService;
5965
_config = _configService.Create();
6066
_wrapperFactory = wrapperFactory;
67+
_messageBox = messageBox;
6168

6269
_initRepoCommand = new DelegateCommand(_ => InitRepo());
6370
_openRepoCommand = new DelegateCommand(_ => OpenRepo());
@@ -87,6 +94,8 @@ public SourceControlViewViewModel(
8794
Status = RubberduckUI.Offline;
8895

8996
ListenForErrors();
97+
98+
_fileSystemWatcher = new FileSystemWatcher();
9099
}
91100

92101
public void SetTab(SourceControlTab tab)
@@ -98,22 +107,47 @@ public void AddComponent(VBComponent component)
98107
{
99108
if (Provider == null) { return; }
100109

110+
_fileSystemWatcher.EnableRaisingEvents = false;
101111
var fileStatus = Provider.Status().SingleOrDefault(stat => stat.FilePath.Split('.')[0] == component.Name);
102112
if (fileStatus != null)
103113
{
104114
Provider.AddFile(fileStatus.FilePath);
105115
}
116+
_fileSystemWatcher.EnableRaisingEvents = true;
106117
}
107118

108119
public void RemoveComponent(VBComponent component)
109120
{
110121
if (Provider == null) { return; }
111122

123+
_fileSystemWatcher.EnableRaisingEvents = false;
112124
var fileStatus = Provider.Status().SingleOrDefault(stat => stat.FilePath.Split('.')[0] == component.Name);
113125
if (fileStatus != null)
114126
{
115127
Provider.RemoveFile(fileStatus.FilePath, true);
116128
}
129+
_fileSystemWatcher.EnableRaisingEvents = true;
130+
}
131+
132+
public void RenameComponent(VBComponent component, string oldName)
133+
{
134+
if (Provider == null) { return; }
135+
136+
var fileStatus = Provider.LastKnownStatus().SingleOrDefault(stat => stat.FilePath.Split('.')[0] == oldName);
137+
if (fileStatus != null)
138+
{
139+
_fileSystemWatcher.EnableRaisingEvents = false;
140+
var directory = Provider.CurrentRepository.LocalLocation;
141+
directory += directory.EndsWith("\\") ? string.Empty : "\\";
142+
143+
var fileExt = "." + fileStatus.FilePath.Split('.').Last();
144+
145+
File.Move(directory + fileStatus.FilePath, directory + component.Name + fileExt);
146+
Provider.RemoveFile(oldName + fileExt, false);
147+
Provider.AddFile(component.Name + fileExt);
148+
149+
_fileSystemWatcher.EnableRaisingEvents = true;
150+
}
117151
}
118152

119153
private static readonly IDictionary<NotificationType, BitmapImage> IconMappings =
@@ -125,7 +159,7 @@ public void RemoveComponent(VBComponent component)
125159

126160
private void _state_StateChanged(object sender, ParserStateEventArgs e)
127161
{
128-
if (e.State == ParserState.Parsed)
162+
if (e.State == ParserState.Pending)
129163
{
130164
UiDispatcher.InvokeAsync(Refresh);
131165
}
@@ -139,6 +173,86 @@ public ISourceControlProvider Provider
139173
{
140174
_provider = value;
141175
SetChildPresenterSourceControlProviders(_provider);
176+
177+
if (_fileSystemWatcher.Path != LocalDirectory)
178+
{
179+
_fileSystemWatcher.Path = _provider.CurrentRepository.LocalLocation;
180+
_fileSystemWatcher.EnableRaisingEvents = true;
181+
_fileSystemWatcher.IncludeSubdirectories = true;
182+
183+
_fileSystemWatcher.Created += _fileSystemWatcher_Created;
184+
_fileSystemWatcher.Deleted += _fileSystemWatcher_Deleted;
185+
_fileSystemWatcher.Renamed += _fileSystemWatcher_Renamed;
186+
_fileSystemWatcher.Changed += _fileSystemWatcher_Changed;
187+
}
188+
}
189+
}
190+
191+
private void _fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
192+
{
193+
// the file system filter doesn't support multiple filters
194+
if (!new[] { "cls", "bas", "frm" }.Contains(e.Name.Split('.').Last()))
195+
{
196+
return;
197+
}
198+
199+
Logger.Trace("File system watcher detected file changed");
200+
if (_messageBox.Show("file changed", RubberduckUI.SourceControlPanel_Caption,
201+
MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1) == DialogResult.OK)
202+
{
203+
UiDispatcher.InvokeAsync(Refresh);
204+
}
205+
}
206+
207+
private void _fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
208+
{
209+
// the file system filter doesn't support multiple filters
210+
if (!new[] { "cls", "bas", "frm" }.Contains(e.Name.Split('.').Last()))
211+
{
212+
return;
213+
}
214+
215+
Logger.Trace("File system watcher detected file renamed");
216+
if (_messageBox.Show("file changed", RubberduckUI.SourceControlPanel_Caption,
217+
MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1) == DialogResult.OK)
218+
{
219+
Provider.RemoveFile(e.OldFullPath, true);
220+
Provider.AddFile(e.FullPath);
221+
UiDispatcher.InvokeAsync(Refresh);
222+
}
223+
}
224+
225+
private void _fileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
226+
{
227+
// the file system filter doesn't support multiple filters
228+
if (!new[] { "cls", "bas", "frm" }.Contains(e.Name.Split('.').Last()))
229+
{
230+
return;
231+
}
232+
233+
Logger.Trace("File system watcher detected file deleted");
234+
if (_messageBox.Show("file changed", RubberduckUI.SourceControlPanel_Caption,
235+
MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1) == DialogResult.OK)
236+
{
237+
Provider.RemoveFile(e.FullPath, true);
238+
UiDispatcher.InvokeAsync(Refresh);
239+
}
240+
}
241+
242+
private void _fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
243+
{
244+
// the file system filter doesn't support multiple filters
245+
if (!new[] { "cls", "bas", "frm" }.Contains(e.Name.Split('.').Last()))
246+
{
247+
return;
248+
}
249+
250+
Logger.Trace("File system watcher detected file created");
251+
if (_messageBox.Show("file changed", RubberduckUI.SourceControlPanel_Caption,
252+
MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1) == DialogResult.OK)
253+
{
254+
Provider.AddFile(e.FullPath);
255+
UiDispatcher.InvokeAsync(Refresh);
142256
}
143257
}
144258

@@ -653,6 +767,7 @@ private void OpenRepoAssignedToProject()
653767

654768
private void Refresh()
655769
{
770+
_fileSystemWatcher.EnableRaisingEvents = false;
656771
if (Provider == null)
657772
{
658773
OpenRepoAssignedToProject();
@@ -664,6 +779,11 @@ private void Refresh()
664779
tab.ViewModel.RefreshView();
665780
}
666781
}
782+
783+
if (Provider != null)
784+
{
785+
_fileSystemWatcher.EnableRaisingEvents = true;
786+
}
667787
}
668788

669789
private bool ValidRepoExists()
@@ -824,6 +944,15 @@ public void Dispose()
824944
{
825945
_state.StateChanged -= _state_StateChanged;
826946
}
947+
948+
if (_fileSystemWatcher != null)
949+
{
950+
_fileSystemWatcher.Created -= _fileSystemWatcher_Created;
951+
_fileSystemWatcher.Deleted -= _fileSystemWatcher_Deleted;
952+
_fileSystemWatcher.Renamed -= _fileSystemWatcher_Renamed;
953+
_fileSystemWatcher.Changed -= _fileSystemWatcher_Changed;
954+
_fileSystemWatcher.Dispose();
955+
}
827956
}
828957
}
829958
}

Rubberduck.SourceControl/GitProvider.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public override IRepository InitVBAProject(string directory)
171171
//add a master branch to newly created repo
172172
using (var repo = new LibGit2Sharp.Repository(repository.LocalLocation))
173173
{
174-
var status = repo.RetrieveStatus(new StatusOptions());
174+
var status = repo.RetrieveStatus(new StatusOptions {DetectRenamesInWorkDir = true});
175175
foreach (var stat in status.Untracked)
176176
{
177177
repo.Stage(stat.FilePath);
@@ -482,7 +482,7 @@ public override IEnumerable<IFileStatusEntry> Status()
482482
try
483483
{
484484
base.Status();
485-
return _repo.RetrieveStatus(new StatusOptions {IncludeUnaltered = true})
485+
return _repo.RetrieveStatus(new StatusOptions {IncludeUnaltered = true, DetectRenamesInWorkDir = true })
486486
.Select(item => new FileStatusEntry(item));
487487
}
488488
catch (LibGit2SharpException ex)
@@ -491,6 +491,19 @@ public override IEnumerable<IFileStatusEntry> Status()
491491
}
492492
}
493493

494+
495+
public override IEnumerable<IFileStatusEntry> LastKnownStatus()
496+
{
497+
try
498+
{
499+
return _repo.RetrieveStatus(new StatusOptions { IncludeUnaltered = true, DetectRenamesInWorkDir = true})
500+
.Select(item => new FileStatusEntry(item));
501+
}
502+
catch (LibGit2SharpException ex)
503+
{
504+
throw new SourceControlException(SourceControlText.GitRepoStatusFailed, ex);
505+
}
506+
}
494507
public override void Undo(string filePath)
495508
{
496509
try

Rubberduck.SourceControl/ISourceControlProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,11 @@ public interface ISourceControlProvider
157157
/// </summary>
158158
/// <returns>Returns true if repo can log into GitHub.</returns>
159159
bool HasCredentials();
160+
161+
/// <summary>
162+
/// Gets the last known status without refreshing
163+
/// </summary>
164+
/// <returns>Collection of statuses.</returns>
165+
IEnumerable<IFileStatusEntry> LastKnownStatus();
160166
}
161167
}

Rubberduck.SourceControl/SourceControlProviderBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ protected SourceControlProviderBase(VBProject project, IRepository repository, I
4444
public abstract void Publish(string branch);
4545
public abstract void Unpublish(string branch);
4646
public abstract bool HasCredentials();
47+
public abstract IEnumerable<IFileStatusEntry> LastKnownStatus();
4748

4849
public virtual void CreateBranch(string sourceBranch, string branch)
4950
{

RubberduckTests/SourceControl/SourceControlViewModelTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ private void SetupVM()
109109
{
110110
_vm = new SourceControlViewViewModel(_vbe.Object, new RubberduckParserState(), _providerFactory.Object, _folderBrowserFactory.Object,
111111
_configService.Object, new ChangesView(_changesVM), new BranchesView(_branchesVM),
112-
new UnsyncedCommitsView(_unsyncedVM), new SettingsView(_settingsVM), new CodePaneWrapperFactory());
112+
new UnsyncedCommitsView(_unsyncedVM), new SettingsView(_settingsVM), new CodePaneWrapperFactory(), new Mock<IMessageBox>().Object);
113113
}
114114

115115
[TestMethod]

0 commit comments

Comments
 (0)