diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs index afe7ca79d4..cb3bab2373 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs @@ -47,7 +47,7 @@ namespace ICSharpCode.AvalonEdit.AddIn drawingContext.DrawRectangle(Brushes.LightGreen, null, rect); break; case ChangeType.Modified: - drawingContext.DrawRectangle(Brushes.Blue, null, rect); + drawingContext.DrawRectangle(Brushes.LightBlue, null, rect); break; case ChangeType.Unsaved: drawingContext.DrawRectangle(Brushes.Yellow, null, rect); diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs index a83bb4988c..021812a7a2 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs @@ -24,6 +24,7 @@ namespace ICSharpCode.AvalonEdit.AddIn CompressingTreeList changeList; IDocument document; TextDocument textDocument; + IDocument baseDocument; public event EventHandler ChangeOccurred; @@ -51,44 +52,69 @@ namespace ICSharpCode.AvalonEdit.AddIn this.textDocument = ((TextView)document.GetService(typeof(TextView))).Document; this.changeList = new CompressingTreeList((x, y) => x.Equals(y)); - SetupInitialFileState(); + Stream baseFileStream = GetBaseVersion(); + + if (baseFileStream != null) + baseDocument = DocumentUtilitites.LoadReadOnlyDocumentFromBuffer(new StringTextBuffer(ReadAll(baseFileStream))); + + SetupInitialFileState(false); lineTracker = WeakLineTracker.Register(this.textDocument, this); this.textDocument.UndoStack.PropertyChanged += UndoStackPropertyChanged; } - void SetupInitialFileState() + LineChangeInfo TransformLineChangeInfo(LineChangeInfo info) { - changeList.Clear(); + if (info.Change == ChangeType.Unsaved) + info.Change = ChangeType.Added; - Stream baseFileStream = GetBaseVersion(); - string baseFile = ReadAll(baseFileStream); - - MyersDiff.MyersDiff diff = new MyersDiff.MyersDiff( - new StringSequence(baseFile), - new StringSequence(textDocument.Text) - ); - - if (diff == null) - changeList.InsertRange(0, document.TotalNumberOfLines + 1, new LineChangeInfo(ChangeType.None, "")); - else { + return info; + } + + void SetupInitialFileState(bool update) + { + if (baseDocument == null) { + if (update) + changeList.Transform(TransformLineChangeInfo); + else + changeList.InsertRange(0, document.TotalNumberOfLines + 1, new LineChangeInfo(ChangeType.None, "")); + }else { + changeList.Clear(); + + MyersDiff.MyersDiff diff = new MyersDiff.MyersDiff( + new DocumentSequence(baseDocument), + new DocumentSequence(document) + ); + changeList.Add(new LineChangeInfo(ChangeType.None, "")); int lastEndLine = 0; + foreach (Edit edit in diff.GetEdits()) { Console.WriteLine(edit); - int beginLine = textDocument.GetLineByOffset(edit.BeginB).LineNumber; - int endLine = textDocument.GetLineByOffset(edit.EndB).LineNumber; - changeList.InsertRange(changeList.Count, beginLine - lastEndLine, new LineChangeInfo(ChangeType.None, "")); + int beginLine = edit.BeginB; + int endLine = edit.EndB; + + changeList.InsertRange(changeList.Count, beginLine - lastEndLine, LineChangeInfo.Empty); + if (edit.EditType == ChangeType.Deleted) { LineChangeInfo change = changeList[beginLine]; - change.DeletedLinesAfterThisLine += baseFile.Substring(edit.BeginA, edit.EndA - edit.BeginA); + + for (int i = edit.BeginA; i < edit.EndA; i++) { + var line = baseDocument.GetLine(i + 1); + change.DeletedLinesAfterThisLine += line.Text; + } + changeList[beginLine] = change; - } else - changeList.InsertRange(changeList.Count, endLine - beginLine, new LineChangeInfo(edit.EditType, "")); + } else { + var change = new LineChangeInfo(edit.EditType, ""); + changeList.InsertRange(changeList.Count, endLine - beginLine, change); + } + lastEndLine = endLine; } - changeList.InsertRange(changeList.Count, textDocument.LineCount - lastEndLine, new LineChangeInfo(ChangeType.None, "")); + + changeList.InsertRange(changeList.Count, textDocument.LineCount - lastEndLine, LineChangeInfo.Empty); } OnChangeOccurred(EventArgs.Empty); @@ -111,30 +137,18 @@ namespace ICSharpCode.AvalonEdit.AddIn return result; } - return new DefaultVersionProvider().OpenBaseVersion(fileName); + return null; } void UndoStackPropertyChanged(object sender, PropertyChangedEventArgs e) { if (textDocument.UndoStack.IsOriginalFile) - SetupInitialFileState(); + SetupInitialFileState(true); } void ILineTracker.BeforeRemoveLine(DocumentLine line) { - int index = line.LineNumber; - LineChangeInfo info = changeList[index]; - LineChangeInfo lineBefore = changeList[index - 1]; - - // TODO : add deleted text (GetText not allowed in ILineTracker callbacks) -// lineBefore.DeletedLinesAfterThisLine -// += (textDocument.GetText(line.Offset, line.Length) -// + Environment.NewLine + info.DeletedLinesAfterThisLine); -// -// Debug.Assert(lineBefore.DeletedLinesAfterThisLine.EndsWith(Environment.NewLine)); - - changeList[index - 1] = lineBefore; - changeList.RemoveAt(index); + changeList.RemoveAt(line.LineNumber); } void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength) @@ -160,6 +174,8 @@ namespace ICSharpCode.AvalonEdit.AddIn void ILineTracker.RebuildDocument() { + changeList.Clear(); + changeList.InsertRange(0, document.TotalNumberOfLines + 1, new LineChangeInfo(ChangeType.Unsaved, "")); } bool disposed = false; diff --git a/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs b/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs index 94b96f8e2f..4c59d29742 100644 --- a/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs +++ b/src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs @@ -87,13 +87,20 @@ namespace ICSharpCode.GitAddIn runner.Start("cmd", "/c git ls-tree HEAD " + Path.GetFileName(fileName)); runner.WaitForExit(); - return runner.StandardOutput - .Trim() - .Split(new[] { " ", "\t" }, StringSplitOptions.RemoveEmptyEntries)[2]; + string output = runner.StandardOutput.Trim(); + string[] parts = output.Split(new[] { " ", "\t" }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length < 3) + return null; + + return parts[2]; } Stream OpenOutput(string fileName, string blobHash) { + if (blobHash == null) + return null; + AnonymousPipeServerStream pipe = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable); StartupInfo startupInfo = new GitVersionProvider.StartupInfo(); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/CompressingTreeList.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/CompressingTreeList.cs index 421282a16f..db23c5bb2c 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/CompressingTreeList.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/CompressingTreeList.cs @@ -353,6 +353,26 @@ namespace ICSharpCode.AvalonEdit.Utils return GetNode(ref index).count - index; } + /// + /// Applies the conversion function to all elements in this CompressingTreeList. + /// + public void Transform(Func converter) + { + if (root == null) + return; + Node prevNode = null; + for (Node n = root.LeftMost; n != null; n = n.Successor) { + n.value = converter(n.value); + if (prevNode != null && comparisonFunc(prevNode.value, n.value)) { + n.count += prevNode.count; + UpdateAugmentedData(n); + RemoveNode(prevNode); + } + prevNode = n; + } + } + + /// /// Inserts the specified at /// diff --git a/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs b/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs index 89cacce1f3..098475e79c 100644 --- a/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs +++ b/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs @@ -64,6 +64,8 @@ namespace ICSharpCode.SharpDevelop.Editor public struct LineChangeInfo : IEquatable { + public static readonly LineChangeInfo Empty = new LineChangeInfo(ChangeType.None, ""); + ChangeType change; public ChangeType Change {