3
3
using System . Collections . ObjectModel ;
4
4
using System . Drawing ;
5
5
using System . Globalization ;
6
+ using System . IO ;
6
7
using System . Linq ;
7
8
using System . Windows . Forms ;
8
9
using System . Windows . Input ;
9
10
using System . Windows . Media . Imaging ;
10
11
using Microsoft . Vbe . Interop ;
11
12
using Ninject ;
13
+ using NLog ;
12
14
using Rubberduck . Parsing . VBA ;
13
15
using Rubberduck . SourceControl ;
14
16
using Rubberduck . UI . Command ;
@@ -35,6 +37,10 @@ public sealed class SourceControlViewViewModel : ViewModelBase, IDisposable
35
37
private readonly ISourceControlConfigProvider _configService ;
36
38
private readonly SourceControlSettings _config ;
37
39
private readonly ICodePaneWrapperFactory _wrapperFactory ;
40
+ private readonly IMessageBox _messageBox ;
41
+ private readonly FileSystemWatcher _fileSystemWatcher ;
42
+ private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
43
+ private static readonly IEnumerable < string > VbFileExtensions = new [ ] { "cls" , "bas" , "frm" } ;
38
44
39
45
public SourceControlViewViewModel (
40
46
VBE vbe ,
@@ -46,7 +52,8 @@ public SourceControlViewViewModel(
46
52
[ Named ( "branchesView" ) ] IControlView branchesView ,
47
53
[ Named ( "unsyncedCommitsView" ) ] IControlView unsyncedCommitsView ,
48
54
[ Named ( "settingsView" ) ] IControlView settingsView ,
49
- ICodePaneWrapperFactory wrapperFactory )
55
+ ICodePaneWrapperFactory wrapperFactory ,
56
+ IMessageBox messageBox )
50
57
{
51
58
_vbe = vbe ;
52
59
_state = state ;
@@ -58,6 +65,7 @@ public SourceControlViewViewModel(
58
65
_configService = configService ;
59
66
_config = _configService . Create ( ) ;
60
67
_wrapperFactory = wrapperFactory ;
68
+ _messageBox = messageBox ;
61
69
62
70
_initRepoCommand = new DelegateCommand ( _ => InitRepo ( ) ) ;
63
71
_openRepoCommand = new DelegateCommand ( _ => OpenRepo ( ) ) ;
@@ -87,16 +95,18 @@ public SourceControlViewViewModel(
87
95
Status = RubberduckUI . Offline ;
88
96
89
97
ListenForErrors ( ) ;
98
+
99
+ _fileSystemWatcher = new FileSystemWatcher ( ) ;
90
100
}
91
101
92
102
public void SetTab ( SourceControlTab tab )
93
103
{
94
104
SelectedItem = TabItems . First ( t => t . ViewModel . Tab == tab ) ;
95
105
}
96
106
97
- public void AddComponent ( VBComponent component )
107
+ public void HandleAddedComponent ( VBComponent component )
98
108
{
99
- if ( Provider == null ) { return ; }
109
+ if ( Provider == null || ! Provider . HandleVbeSinkEvents ) { return ; }
100
110
101
111
var fileStatus = Provider . Status ( ) . SingleOrDefault ( stat => stat . FilePath . Split ( '.' ) [ 0 ] == component . Name ) ;
102
112
if ( fileStatus != null )
@@ -105,9 +115,9 @@ public void AddComponent(VBComponent component)
105
115
}
106
116
}
107
117
108
- public void RemoveComponent ( VBComponent component )
118
+ public void HandleRemovedComponent ( VBComponent component )
109
119
{
110
- if ( Provider == null ) { return ; }
120
+ if ( Provider == null || ! Provider . HandleVbeSinkEvents ) { return ; }
111
121
112
122
var fileStatus = Provider . Status ( ) . SingleOrDefault ( stat => stat . FilePath . Split ( '.' ) [ 0 ] == component . Name ) ;
113
123
if ( fileStatus != null )
@@ -116,6 +126,27 @@ public void RemoveComponent(VBComponent component)
116
126
}
117
127
}
118
128
129
+ public void HandleRenamedComponent ( VBComponent component , string oldName )
130
+ {
131
+ if ( Provider == null || ! Provider . HandleVbeSinkEvents ) { return ; }
132
+
133
+ var fileStatus = Provider . LastKnownStatus ( ) . SingleOrDefault ( stat => stat . FilePath . Split ( '.' ) [ 0 ] == oldName ) ;
134
+ if ( fileStatus != null )
135
+ {
136
+ var directory = Provider . CurrentRepository . LocalLocation ;
137
+ directory += directory . EndsWith ( "\\ " ) ? string . Empty : "\\ " ;
138
+
139
+ var fileExt = "." + fileStatus . FilePath . Split ( '.' ) . Last ( ) ;
140
+
141
+ _fileSystemWatcher . EnableRaisingEvents = false ;
142
+ File . Move ( directory + fileStatus . FilePath , directory + component . Name + fileExt ) ;
143
+ _fileSystemWatcher . EnableRaisingEvents = true ;
144
+
145
+ Provider . RemoveFile ( oldName + fileExt , false ) ;
146
+ Provider . AddFile ( component . Name + fileExt ) ;
147
+ }
148
+ }
149
+
119
150
private static readonly IDictionary < NotificationType , BitmapImage > IconMappings =
120
151
new Dictionary < NotificationType , BitmapImage >
121
152
{
@@ -125,7 +156,7 @@ public void RemoveComponent(VBComponent component)
125
156
126
157
private void _state_StateChanged ( object sender , ParserStateEventArgs e )
127
158
{
128
- if ( e . State == ParserState . Parsed )
159
+ if ( e . State == ParserState . Pending )
129
160
{
130
161
UiDispatcher . InvokeAsync ( Refresh ) ;
131
162
}
@@ -139,6 +170,107 @@ public ISourceControlProvider Provider
139
170
{
140
171
_provider = value ;
141
172
SetChildPresenterSourceControlProviders ( _provider ) ;
173
+
174
+ if ( _fileSystemWatcher . Path != LocalDirectory && Directory . Exists ( _provider . CurrentRepository . LocalLocation ) )
175
+ {
176
+ _fileSystemWatcher . Path = _provider . CurrentRepository . LocalLocation ;
177
+ _fileSystemWatcher . EnableRaisingEvents = true ;
178
+ _fileSystemWatcher . IncludeSubdirectories = true ;
179
+
180
+ _fileSystemWatcher . Created += _fileSystemWatcher_Created ;
181
+ _fileSystemWatcher . Deleted += _fileSystemWatcher_Deleted ;
182
+ _fileSystemWatcher . Renamed += _fileSystemWatcher_Renamed ;
183
+ _fileSystemWatcher . Changed += _fileSystemWatcher_Changed ;
184
+ }
185
+ }
186
+ }
187
+
188
+ private void _fileSystemWatcher_Changed ( object sender , FileSystemEventArgs e )
189
+ {
190
+ // the file system filter doesn't support multiple filters
191
+ if ( ! VbFileExtensions . Contains ( e . Name . Split ( '.' ) . Last ( ) ) )
192
+ {
193
+ return ;
194
+ }
195
+
196
+ if ( ! Provider . NotifyExternalFileChanges )
197
+ {
198
+ return ;
199
+ }
200
+
201
+ Logger . Trace ( "File system watcher detected file changed" ) ;
202
+ if ( _messageBox . Show ( RubberduckUI . SourceControl_ExternalModifications , RubberduckUI . SourceControlPanel_Caption ,
203
+ MessageBoxButtons . OKCancel , MessageBoxIcon . Information , MessageBoxDefaultButton . Button1 ) == DialogResult . OK )
204
+ {
205
+ Provider . ReloadComponent ( e . Name ) ;
206
+ UiDispatcher . InvokeAsync ( Refresh ) ;
207
+ }
208
+ }
209
+
210
+ private void _fileSystemWatcher_Renamed ( object sender , RenamedEventArgs e )
211
+ {
212
+ // the file system filter doesn't support multiple filters
213
+ if ( ! VbFileExtensions . Contains ( e . Name . Split ( '.' ) . Last ( ) ) )
214
+ {
215
+ return ;
216
+ }
217
+
218
+ if ( ! Provider . NotifyExternalFileChanges )
219
+ {
220
+ return ;
221
+ }
222
+
223
+ Logger . Trace ( "File system watcher detected file renamed" ) ;
224
+ if ( _messageBox . Show ( RubberduckUI . SourceControl_ExternalModifications , RubberduckUI . SourceControlPanel_Caption ,
225
+ MessageBoxButtons . OKCancel , MessageBoxIcon . Information , MessageBoxDefaultButton . Button1 ) == DialogResult . OK )
226
+ {
227
+ Provider . RemoveFile ( e . OldFullPath , true ) ;
228
+ Provider . AddFile ( e . FullPath ) ;
229
+ UiDispatcher . InvokeAsync ( Refresh ) ;
230
+ }
231
+ }
232
+
233
+ private void _fileSystemWatcher_Deleted ( object sender , FileSystemEventArgs e )
234
+ {
235
+ // the file system filter doesn't support multiple filters
236
+ if ( ! VbFileExtensions . Contains ( e . Name . Split ( '.' ) . Last ( ) ) )
237
+ {
238
+ return ;
239
+ }
240
+
241
+ if ( ! Provider . NotifyExternalFileChanges )
242
+ {
243
+ return ;
244
+ }
245
+
246
+ Logger . Trace ( "File system watcher detected file deleted" ) ;
247
+ if ( _messageBox . Show ( RubberduckUI . SourceControl_ExternalModifications , RubberduckUI . SourceControlPanel_Caption ,
248
+ MessageBoxButtons . OKCancel , MessageBoxIcon . Information , MessageBoxDefaultButton . Button1 ) == DialogResult . OK )
249
+ {
250
+ Provider . RemoveFile ( e . FullPath , true ) ;
251
+ UiDispatcher . InvokeAsync ( Refresh ) ;
252
+ }
253
+ }
254
+
255
+ private void _fileSystemWatcher_Created ( object sender , FileSystemEventArgs e )
256
+ {
257
+ // the file system filter doesn't support multiple filters
258
+ if ( ! VbFileExtensions . Contains ( e . Name . Split ( '.' ) . Last ( ) ) )
259
+ {
260
+ return ;
261
+ }
262
+
263
+ if ( ! Provider . NotifyExternalFileChanges )
264
+ {
265
+ return ;
266
+ }
267
+
268
+ Logger . Trace ( "File system watcher detected file created" ) ;
269
+ if ( _messageBox . Show ( RubberduckUI . SourceControl_ExternalModifications , RubberduckUI . SourceControlPanel_Caption ,
270
+ MessageBoxButtons . OKCancel , MessageBoxIcon . Information , MessageBoxDefaultButton . Button1 ) == DialogResult . OK )
271
+ {
272
+ Provider . AddFile ( e . FullPath ) ;
273
+ UiDispatcher . InvokeAsync ( Refresh ) ;
142
274
}
143
275
}
144
276
@@ -653,6 +785,7 @@ private void OpenRepoAssignedToProject()
653
785
654
786
private void Refresh ( )
655
787
{
788
+ _fileSystemWatcher . EnableRaisingEvents = false ;
656
789
if ( Provider == null )
657
790
{
658
791
OpenRepoAssignedToProject ( ) ;
@@ -664,6 +797,11 @@ private void Refresh()
664
797
tab . ViewModel . RefreshView ( ) ;
665
798
}
666
799
}
800
+
801
+ if ( Provider != null && Directory . Exists ( Provider . CurrentRepository . LocalLocation ) )
802
+ {
803
+ _fileSystemWatcher . EnableRaisingEvents = true ;
804
+ }
667
805
}
668
806
669
807
private bool ValidRepoExists ( )
@@ -824,6 +962,15 @@ public void Dispose()
824
962
{
825
963
_state . StateChanged -= _state_StateChanged ;
826
964
}
965
+
966
+ if ( _fileSystemWatcher != null )
967
+ {
968
+ _fileSystemWatcher . Created -= _fileSystemWatcher_Created ;
969
+ _fileSystemWatcher . Deleted -= _fileSystemWatcher_Deleted ;
970
+ _fileSystemWatcher . Renamed -= _fileSystemWatcher_Renamed ;
971
+ _fileSystemWatcher . Changed -= _fileSystemWatcher_Changed ;
972
+ _fileSystemWatcher . Dispose ( ) ;
973
+ }
827
974
}
828
975
}
829
976
}
0 commit comments