diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs index 83626a33b3..8379c1ca13 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs @@ -2,24 +2,18 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Threading; -using ICSharpCode.AvalonEdit.AddIn.Options; + using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Rendering; -using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Editor.AvalonEdit; -using ICSharpCode.SharpDevelop.Widgets; namespace ICSharpCode.AvalonEdit.AddIn { diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs index 34b258b58f..1898aa4f35 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs @@ -6,14 +6,12 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; -using System.Runtime.InteropServices; -using System.Text; + using ICSharpCode.AvalonEdit.AddIn.MyersDiff; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Editor.AvalonEdit; namespace ICSharpCode.AvalonEdit.AddIn { @@ -23,6 +21,8 @@ namespace ICSharpCode.AvalonEdit.AddIn IDocument document; TextDocument textDocument; IDocument baseDocument; + IDocumentVersionProvider usedProvider; + IDisposable watcher; public event EventHandler ChangeOccurred; @@ -47,23 +47,38 @@ namespace ICSharpCode.AvalonEdit.AddIn this.textDocument = (TextDocument)document.GetService(typeof(TextDocument)); this.changeList = new CompressingTreeList((x, y) => x.Equals(y)); - Stream baseFileStream = GetBaseVersion(); + InitializeBaseDocument(); + + if (usedProvider != null) { + string fileName = ((ITextEditor)document.GetService(typeof(ITextEditor))).FileName; + watcher = usedProvider.WatchBaseVersionChanges(fileName, HandleBaseVersionChanges); + } + + SetupInitialFileState(false); - // TODO : update baseDocument on VCS actions + this.textDocument.LineTrackers.Add(this); + this.textDocument.UndoStack.PropertyChanged += UndoStackPropertyChanged; + } + + void HandleBaseVersionChanges(object sender, EventArgs e) + { + ICSharpCode.Core.LoggingService.Info("HandleBaseVersionChanges"); + InitializeBaseDocument(); + SetupInitialFileState(true); + } + + void InitializeBaseDocument() + { + Stream baseFileStream = GetBaseVersion(); if (baseFileStream != null) { // ReadAll() is taking care of closing the stream baseDocument = DocumentUtilitites.LoadReadOnlyDocumentFromBuffer(new StringTextBuffer(ReadAll(baseFileStream))); } else { + // if the file is not under subversion, the document is the opened document if (baseDocument == null) { - // if the file is not under subversion, the document is the opened document baseDocument = DocumentUtilitites.LoadReadOnlyDocumentFromBuffer(document.CreateSnapshot()); } } - - SetupInitialFileState(false); - - this.textDocument.LineTrackers.Add(this); - this.textDocument.UndoStack.PropertyChanged += UndoStackPropertyChanged; } LineChangeInfo TransformLineChangeInfo(LineChangeInfo info) @@ -115,9 +130,11 @@ namespace ICSharpCode.AvalonEdit.AddIn string ReadAll(Stream stream) { - using (StreamReader reader = new StreamReader(stream)) { - return reader.ReadToEnd(); - } + var memory = new MemoryStream(); + stream.CopyTo(memory); + stream.Close(); + memory.Position = 0; + return FileReader.ReadFileContent(memory, ParserService.DefaultFileEncoding); } Stream GetBaseVersion() @@ -126,10 +143,12 @@ namespace ICSharpCode.AvalonEdit.AddIn foreach (IDocumentVersionProvider provider in VersioningServices.Instance.DocumentVersionProviders) { var result = provider.OpenBaseVersion(fileName); - if (result != null) + if (result != null) { + usedProvider = provider; return result; + } } - + return null; } @@ -172,6 +191,8 @@ namespace ICSharpCode.AvalonEdit.AddIn public void Dispose() { if (!disposed) { + if (watcher != null) + watcher.Dispose(); this.textDocument.LineTrackers.Remove(this); this.textDocument.UndoStack.PropertyChanged -= UndoStackPropertyChanged; disposed = true; diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/LineChangeInfo.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/LineChangeInfo.cs index ad8c7e7014..cba099d4b6 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/LineChangeInfo.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/LineChangeInfo.cs @@ -2,8 +2,6 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; -using System.Collections.Generic; -using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.SharpDevelop.Editor; namespace ICSharpCode.AvalonEdit.AddIn diff --git a/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs b/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs index 39ce3bbea4..f10b4cf959 100644 --- a/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs +++ b/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs @@ -7,6 +7,7 @@ using System.IO; using System.IO.Pipes; using System.Runtime.InteropServices; +using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Util; using Microsoft.Win32.SafeHandles; @@ -85,7 +86,7 @@ namespace ICSharpCode.GitAddIn return OpenOutput(git, fileName, GetBlobHash(git, fileName)); } - string GetBlobHash(string gitExe, string fileName) + internal static string GetBlobHash(string gitExe, string fileName) { ProcessRunner runner = new ProcessRunner(); runner.WorkingDirectory = Path.GetDirectoryName(fileName); @@ -136,5 +137,54 @@ namespace ICSharpCode.GitAddIn return pipe; } + + public IDisposable WatchBaseVersionChanges(string fileName, EventHandler callback) + { + if (!Git.IsInWorkingCopy(fileName)) + return null; + + string git = Git.FindGit(); + if (git == null) + return null; + + return new BaseVersionChangeWatcher(fileName, GetBlobHash(git, fileName), callback); + } } + + class BaseVersionChangeWatcher : IDisposable + { + EventHandler callback; + string fileName, hash; + RepoChangeWatcher watcher; + + public BaseVersionChangeWatcher(string fileName, string hash, EventHandler callback) + { + string root = Git.FindWorkingCopyRoot(fileName); + if (root == null) + throw new InvalidOperationException(fileName + " must be under version control!"); + + this.callback = callback; + this.fileName = fileName; + this.hash = hash; + + watcher = RepoChangeWatcher.AddWatch(Path.Combine(root, ".git"), HandleChanges); + } + + void HandleChanges() + { + string newHash = GitVersionProvider.GetBlobHash(Git.FindGit(), fileName); + if (newHash != hash) { + LoggingService.Info(fileName + " was changed!"); + callback(this, EventArgs.Empty); + } + this.hash = newHash; + } + + public void Dispose() + { + watcher.ReleaseWatch(HandleChanges); + } + } + + } diff --git a/src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs b/src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs index 45c91d2329..7d103b0b9b 100644 --- a/src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs +++ b/src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs @@ -19,5 +19,10 @@ namespace ICSharpCode.Svn using (SvnClientWrapper client = new SvnClientWrapper()) return client.OpenBaseVersion(fileName); } + + public IDisposable WatchBaseVersionChanges(string fileName, EventHandler callback) + { + return null; + } } } diff --git a/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs b/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs index 0a118f3a26..e72865cb1c 100644 --- a/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs +++ b/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.SharpDevelop.Editor { @@ -16,6 +17,8 @@ namespace ICSharpCode.SharpDevelop.Editor /// to disk or a base version provided by any VCS. /// Stream OpenBaseVersion(string fileName); + + IDisposable WatchBaseVersionChanges(string fileName, EventHandler callback); } public class VersioningServices @@ -34,4 +37,77 @@ namespace ICSharpCode.SharpDevelop.Editor } } + public class RepoChangeWatcher + { + static readonly Dictionary watchers + = new Dictionary(StringComparer.OrdinalIgnoreCase); + + Action actions; + FileSystemWatcher watcher; + + RepoChangeWatcher(string repositoryRoot) + { + this.watcher = new FileSystemWatcher(repositoryRoot); + + if (WorkbenchSingleton.Workbench != null) + watcher.SynchronizingObject = WorkbenchSingleton.Workbench.SynchronizingObject; + + WorkbenchSingleton.MainWindow.Activated += MainWindowActivated; + + watcher.Created += FileChanged; + watcher.Deleted += FileChanged; + watcher.Changed += FileChanged; + watcher.Renamed += FileChanged; + +// watcher.IncludeSubdirectories = true; + watcher.EnableRaisingEvents = true; + } + + void MainWindowActivated(object sender, EventArgs e) + { + alreadyCalled = false; + actions(); + } + + bool alreadyCalled; + + void FileChanged(object sender, FileSystemEventArgs e) + { + if (!alreadyCalled) { + alreadyCalled = true; + LoggingService.Info(e.Name + " changed!" + e.ChangeType); + if (WorkbenchSingleton.Workbench.IsActiveWindow) { + WorkbenchSingleton.CallLater( + TimeSpan.FromSeconds(2), + () => { MainWindowActivated(this, EventArgs.Empty); } + ); + } + } + } + + public static RepoChangeWatcher AddWatch(string repositoryRoot, Action action) + { + RepoChangeWatcher watcher; + if (!watchers.TryGetValue(repositoryRoot, out watcher)) { + watcher = new RepoChangeWatcher(repositoryRoot); + watchers.Add(repositoryRoot, watcher); + } + + watcher.actions += action; + return watcher; + } + + bool disposed; + + public void ReleaseWatch(Action action) + { + actions -= action; + if (actions == null && !disposed) { + WorkbenchSingleton.MainWindow.Activated -= MainWindowActivated; + watchers.Remove(watcher.Path); + this.watcher.Dispose(); + disposed = true; + } + } + } }