Browse Source

Merge branch 'changewatcher'

Conflicts:
	SharpDevelop.sln
	src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
pull/15/head
Siegfried Pammer 15 years ago
parent
commit
d6691f2d68
  1. 11
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
  2. 139
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs
  3. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  4. 192
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs
  5. 43
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/DocumentSequence.cs
  6. 204
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/Edit.cs
  7. 89
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/ISequence.cs
  8. 594
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/MyersDiff.cs
  9. 34
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/StringSequence.cs
  10. 22
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/Utils.cs
  11. 5
      src/AddIns/VersionControl/GitAddIn/GitAddIn.addin
  12. 1
      src/AddIns/VersionControl/GitAddIn/GitAddIn.csproj
  13. 128
      src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs
  14. 5
      src/AddIns/VersionControl/SubversionAddIn/ICSharpCode.Svn.addin
  15. 8
      src/AddIns/VersionControl/SubversionAddIn/Src/Gui/HistoryViewDisplayBinding/HistoryViewDisplayBinding.cs
  16. 28
      src/AddIns/VersionControl/SubversionAddIn/Src/SvnClientWrapper.cs
  17. 23
      src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs
  18. 1
      src/AddIns/VersionControl/SubversionAddIn/SubversionAddIn.csproj
  19. 20
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/CompressingTreeList.cs
  20. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  21. 122
      src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs

11
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj

@ -84,6 +84,7 @@ @@ -84,6 +84,7 @@
<Compile Include="Src\AvalonEditViewContent.cs" />
<Compile Include="Src\BracketHighlightRenderer.cs" />
<Compile Include="Src\CaretReferencesRenderer.cs" />
<Compile Include="Src\ChangeMarkerMargin.cs" />
<Compile Include="Src\ChooseEncodingDialog.xaml.cs">
<DependentUpon>ChooseEncodingDialog.xaml</DependentUpon>
<SubType>Code</SubType>
@ -93,9 +94,16 @@ @@ -93,9 +94,16 @@
<Compile Include="Src\CodeEditorAdapter.cs" />
<Compile Include="Src\CodeEditorView.cs" />
<Compile Include="Src\ContextActionsRenderer.cs" />
<Compile Include="Src\DefaultChangeWatcher.cs" />
<Compile Include="Src\ExpressionHighlightRenderer.cs" />
<Compile Include="Src\CodeManipulation.cs" />
<Compile Include="Src\CaretHighlightAdorner.cs" />
<Compile Include="Src\MyersDiff\DocumentSequence.cs" />
<Compile Include="Src\MyersDiff\Edit.cs" />
<Compile Include="Src\MyersDiff\ISequence.cs" />
<Compile Include="Src\MyersDiff\MyersDiff.cs" />
<Compile Include="Src\MyersDiff\StringSequence.cs" />
<Compile Include="Src\MyersDiff\Utils.cs" />
<Compile Include="Src\NewLineConsistencyCheck.cs" />
<Compile Include="Src\SharpDevelopTextEditor.cs" />
<Compile Include="Src\Commands\FoldingCommands.cs" />
@ -219,4 +227,7 @@ @@ -219,4 +227,7 @@
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Src\MyersDiff" />
</ItemGroup>
</Project>

139
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs

@ -0,0 +1,139 @@ @@ -0,0 +1,139 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.AvalonEdit.AddIn
{
/// <summary>
/// Description of ChangeMarkerMargin.
/// </summary>
public class ChangeMarkerMargin : AbstractMargin, IDisposable
{
IChangeWatcher changeWatcher;
public ChangeMarkerMargin()
{
changeWatcher = new DefaultChangeWatcher();
changeWatcher.ChangeOccurred += new EventHandler(ChangeOccurred);
}
protected override void OnRender(DrawingContext drawingContext)
{
Size renderSize = this.RenderSize;
TextView textView = this.TextView;
if (textView != null && textView.VisualLinesValid) {
ITextEditor editor = textView.Services.GetService(typeof(ITextEditor)) as ITextEditor;
changeWatcher.Initialize(editor.Document);
foreach (VisualLine line in textView.VisualLines) {
Rect rect = new Rect(0, line.VisualTop - textView.ScrollOffset.Y, 5, line.Height);
LineChangeInfo info = changeWatcher.GetChange(editor.Document.GetLine(line.FirstDocumentLine.LineNumber));
switch (info.Change) {
case ChangeType.None:
break;
case ChangeType.Added:
drawingContext.DrawRectangle(Brushes.LightGreen, null, rect);
break;
case ChangeType.Modified:
drawingContext.DrawRectangle(Brushes.LightBlue, null, rect);
break;
case ChangeType.Unsaved:
drawingContext.DrawRectangle(Brushes.Yellow, null, rect);
break;
default:
throw new Exception("Invalid value for ChangeType");
}
if (!string.IsNullOrEmpty(info.DeletedLinesAfterThisLine)) {
Point pt1 = new Point(5, line.VisualTop + line.Height - textView.ScrollOffset.Y - 4);
Point pt2 = new Point(10, line.VisualTop + line.Height - textView.ScrollOffset.Y);
Point pt3 = new Point(5, line.VisualTop + line.Height - textView.ScrollOffset.Y + 4);
drawingContext.DrawGeometry(Brushes.Red, null, new PathGeometry(new List<PathFigure>() { CreateNAngle(pt1, pt2, pt3) }));
}
// special case for line 0
if (line.FirstDocumentLine.LineNumber == 1) {
info = changeWatcher.GetChange(null);
if (!string.IsNullOrEmpty(info.DeletedLinesAfterThisLine)) {
Point pt1 = new Point(5, line.VisualTop - textView.ScrollOffset.Y - 4);
Point pt2 = new Point(10, line.VisualTop - textView.ScrollOffset.Y);
Point pt3 = new Point(5, line.VisualTop - textView.ScrollOffset.Y + 4);
drawingContext.DrawGeometry(Brushes.Red, null, new PathGeometry(new List<PathFigure>() { CreateNAngle(pt1, pt2, pt3) }));
}
}
}
}
}
PathFigure CreateNAngle(params Point[] points)
{
if (points == null || points.Length == 0)
return new PathFigure();
List<PathSegment> segs = new List<PathSegment>();
PathSegment seg = new PolyLineSegment(points, true);
segs.Add(seg);
return new PathFigure(points[0], segs, true);
}
protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
{
if (oldTextView != null) {
oldTextView.VisualLinesChanged -= VisualLinesChanged;
oldTextView.ScrollOffsetChanged -= ScrollOffsetChanged;
}
if (newTextView != null) {
newTextView.VisualLinesChanged += VisualLinesChanged;
newTextView.ScrollOffsetChanged += ScrollOffsetChanged;
}
}
void ChangeOccurred(object sender, EventArgs e)
{
InvalidateVisual();
}
void VisualLinesChanged(object sender, EventArgs e)
{
InvalidateVisual();
}
void ScrollOffsetChanged(object sender, EventArgs e)
{
InvalidateVisual();
}
protected override Size MeasureOverride(Size availableSize)
{
return new Size(5, 0);
}
bool disposed = false;
public void Dispose()
{
if (!disposed) {
OnTextViewChanged(TextView, null);
changeWatcher.Dispose();
changeWatcher = null;
disposed = true;
}
}
}
}

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -199,6 +199,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -199,6 +199,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
textView.Services.AddService(typeof(IBookmarkMargin), iconBarManager);
codeEditorView.TextArea.LeftMargins.Insert(0, new IconBarMargin(iconBarManager));
codeEditorView.TextArea.LeftMargins.Add(new ChangeMarkerMargin());
textView.Services.AddService(typeof(ISyntaxHighlighter), new AvalonEditSyntaxHighlighterAdapter(textView));
codeEditorView.TextArea.MouseRightButtonDown += TextAreaMouseRightButtonDown;

192
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs

@ -0,0 +1,192 @@ @@ -0,0 +1,192 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.AvalonEdit.AddIn.MyersDiff;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.AvalonEdit.AddIn
{
public class DefaultChangeWatcher : IChangeWatcher, ILineTracker
{
WeakLineTracker lineTracker;
CompressingTreeList<LineChangeInfo> changeList;
IDocument document;
TextDocument textDocument;
IDocument baseDocument;
public event EventHandler ChangeOccurred;
protected void OnChangeOccurred(EventArgs e)
{
if (ChangeOccurred != null) {
ChangeOccurred(this, e);
}
}
public LineChangeInfo GetChange(IDocumentLine line)
{
if (line == null)
return changeList[0];
return changeList[line.LineNumber];
}
public void Initialize(IDocument document)
{
if (changeList != null && changeList.Any())
return;
this.document = document;
this.textDocument = ((TextView)document.GetService(typeof(TextView))).Document;
this.changeList = new CompressingTreeList<LineChangeInfo>((x, y) => x.Equals(y));
Stream baseFileStream = GetBaseVersion();
// TODO : update baseDocument on VCS actions
if (baseFileStream != null) {
baseDocument = DocumentUtilitites.LoadReadOnlyDocumentFromBuffer(new StringTextBuffer(ReadAll(baseFileStream)));
}
SetupInitialFileState(false);
lineTracker = WeakLineTracker.Register(this.textDocument, this);
this.textDocument.UndoStack.PropertyChanged += UndoStackPropertyChanged;
}
LineChangeInfo TransformLineChangeInfo(LineChangeInfo info)
{
if (info.Change == ChangeType.Unsaved)
info.Change = ChangeType.Added;
return info;
}
void SetupInitialFileState(bool update)
{
if (baseDocument == null) {
if (update)
changeList.Transform(TransformLineChangeInfo);
else
changeList.InsertRange(0, document.TotalNumberOfLines + 1, LineChangeInfo.Empty);
} 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()) {
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];
for (int i = edit.BeginA; i < edit.EndA; i++) {
var line = baseDocument.GetLine(i + 1);
change.DeletedLinesAfterThisLine += line.Text;
}
changeList[beginLine] = change;
} else {
var change = new LineChangeInfo(edit.EditType, "");
changeList.InsertRange(changeList.Count, endLine - beginLine, change);
}
lastEndLine = endLine;
}
changeList.InsertRange(changeList.Count, textDocument.LineCount - lastEndLine, LineChangeInfo.Empty);
}
OnChangeOccurred(EventArgs.Empty);
}
string ReadAll(Stream stream)
{
using (StreamReader reader = new StreamReader(stream)) {
return reader.ReadToEnd();
}
}
Stream GetBaseVersion()
{
string fileName = ((ITextEditor)document.GetService(typeof(ITextEditor))).FileName;
foreach (IDocumentVersionProvider provider in VersioningServices.Instance.DocumentVersionProviders) {
var result = provider.OpenBaseVersion(fileName);
if (result != null)
return result;
}
return null;
}
void UndoStackPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (textDocument.UndoStack.IsOriginalFile)
SetupInitialFileState(true);
}
void ILineTracker.BeforeRemoveLine(DocumentLine line)
{
changeList.RemoveAt(line.LineNumber);
}
void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength)
{
int index = line.LineNumber;
var info = changeList[index];
info.Change = ChangeType.Unsaved;
changeList[index] = info;
}
void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine)
{
int index = insertionPos.LineNumber;
var firstLine = changeList[index];
var newLineInfo = new LineChangeInfo(ChangeType.Unsaved, firstLine.DeletedLinesAfterThisLine);
firstLine.Change = ChangeType.Unsaved;
firstLine.DeletedLinesAfterThisLine = "";
changeList.Insert(index + 1, newLineInfo);
changeList[index] = firstLine;
}
void ILineTracker.RebuildDocument()
{
changeList.Clear();
changeList.InsertRange(0, document.TotalNumberOfLines + 1, new LineChangeInfo(ChangeType.Unsaved, ""));
}
bool disposed = false;
public void Dispose()
{
if (!disposed) {
lineTracker.Deregister();
this.textDocument.UndoStack.PropertyChanged -= UndoStackPropertyChanged;
disposed = true;
}
}
}
}

43
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/DocumentSequence.cs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.AvalonEdit.AddIn.MyersDiff
{
public class DocumentSequence : ISequence
{
IDocument document;
List<int> hashCodes;
public DocumentSequence(IDocument document)
{
this.document = document;
this.hashCodes = new List<int>();
for (int i = 1; i <= document.TotalNumberOfLines; i++) {
hashCodes.Add(document.GetLine(i).Text.GetHashCode());
}
}
public int Size()
{
return document.TotalNumberOfLines;
}
public bool Equals(int i, ISequence other, int j)
{
DocumentSequence seq = other as DocumentSequence;
if (seq == null)
return false;
int thisLineHash = hashCodes[i];
int otherLineHash = seq.hashCodes[j];
return thisLineHash == otherLineHash;
}
}
}

204
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/Edit.cs

@ -0,0 +1,204 @@ @@ -0,0 +1,204 @@
/*
* Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* Copyright (C) 2009, Gil Ran <gilrun@gmail.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Git Development Community nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.AvalonEdit.AddIn.MyersDiff
{
/// <summary>
/// A modified region detected between two versions of roughly the same content.
/// <para />
/// Regions should be specified using 0 based notation, so add 1 to the
/// start and end marks for line numbers in a file.
/// <para />
/// An edit where <code>beginA == endA &amp;&amp; beginB &gt; endB</code> is an insert edit,
/// that is sequence B inserted the elements in region
/// <code>[beginB, endB)</code> at <code>beginA</code>.
/// <para />
/// An edit where <code>beginA &gt; endA &amp;&amp; beginB &gt; endB</code> is a replace edit,
/// that is sequence B has replaced the range of elements between
/// <code>[beginA, endA)</code> with those found in <code>[beginB, endB)</code>.
/// </summary>
public class Edit
{
/// <summary>
/// Create a new empty edit.
/// </summary>
/// <param name="aStart">beginA: start and end of region in sequence A; 0 based.</param>
/// <param name="bStart">beginB: start and end of region in sequence B; 0 based.</param>
public Edit(int aStart, int bStart)
: this(aStart, aStart, bStart, bStart)
{
}
/// <summary>
/// Create a new empty edit.
/// </summary>
/// <param name="aStart">beginA: start and end of region in sequence A; 0 based.</param>
/// <param name="aEnd">endA: end of region in sequence A; must be >= as.</param>
/// <param name="bStart">beginB: start and end of region in sequence B; 0 based.</param>
/// <param name="bEnd">endB: end of region in sequence B; must be >= bs.</param>
public Edit(int aStart, int aEnd, int bStart, int bEnd)
{
BeginA = aStart;
EndA = aEnd;
BeginB = bStart;
EndB = bEnd;
}
/// <summary>
/// Gets the type of this region.
/// </summary>
public ChangeType EditType
{
get
{
if (BeginA == EndA && BeginB < EndB)
return ChangeType.Added;
if (BeginA < EndA && BeginB == EndB)
return ChangeType.Deleted;
if (BeginA == EndA && BeginB == EndB)
return ChangeType.None;
return ChangeType.Modified;
}
}
/// <summary>
/// Start point in sequence A.
/// </summary>
public int BeginA { get; set; }
/// <summary>
/// End point in sequence A.
/// </summary>
public int EndA { get; private set; }
/// <summary>
/// Start point in sequence B.
/// </summary>
public int BeginB { get; private set; }
/// <summary>
/// End point in sequence B.
/// </summary>
public int EndB { get; private set; }
/// <summary>
/// Increase <see cref="EndA"/> by 1.
/// </summary>
public void ExtendA()
{
EndA++;
}
/// <summary>
/// Increase <see cref="EndB"/> by 1.
/// </summary>
public void ExtendB()
{
EndB++;
}
/// <summary>
/// Swap A and B, so the edit goes the other direction.
/// </summary>
public void Swap()
{
int sBegin = BeginA;
int sEnd = EndA;
BeginA = BeginB;
EndA = EndB;
BeginB = sBegin;
EndB = sEnd;
}
public override int GetHashCode()
{
return BeginA ^ EndA;
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is
/// equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified <see cref="T:System.Object"/> is equal to the
/// current <see cref="T:System.Object"/>; otherwise, false.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with
/// the current <see cref="T:System.Object"/>.
/// </param>
/// <exception cref="T:System.NullReferenceException">
/// The <paramref name="obj"/> parameter is null.
/// </exception>
/// <filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
Edit e = (obj as Edit);
if (e != null)
{
return BeginA == e.BeginA && EndA == e.EndA && BeginB == e.BeginB && EndB == e.EndB;
}
return false;
}
public static bool operator ==(Edit left, Edit right)
{
return Equals(left, right);
}
public static bool operator !=(Edit left, Edit right)
{
return !Equals(left, right);
}
public override string ToString()
{
ChangeType t = EditType;
return t + "(" + BeginA + "-" + EndA + "," + BeginB + "-" + EndB + ")";
}
}
}

89
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/ISequence.cs

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
/*
* Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* Copyright (C) 2009, Gil Ran <gilrun@gmail.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Git Development Community nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
namespace ICSharpCode.AvalonEdit.AddIn.MyersDiff
{
/// <summary>
/// Arbitrary sequence of elements with fast comparison support.
/// <para />
/// A sequence of elements is defined to contain elements in the index range
/// <code>[0, <seealso cref="Size()"/>)</code>, like a standard Java List implementation.
/// Unlike a List, the members of the sequence are not directly obtainable, but
/// element equality can be tested if two Sequences are the same implementation.
/// <para />
/// An implementation may chose to implement the equals semantic as necessary,
/// including fuzzy matching rules such as ignoring insignificant sub-elements,
/// e.g. ignoring whitespace differences in text.
/// <para />
/// Implementations of Sequence are primarily intended for use in content
/// difference detection algorithms, to produce an list of
/// <seealso cref="Edit"/> instances describing how two Sequence instances differ.
/// </summary>
public interface ISequence
{
/// <returns>
/// Total number of items in the sequence.
/// </returns>
int Size();
/// <summary>
/// Determine if the i-th member is equal to the j-th member.
/// <para />
/// Implementations must ensure <code>equals(thisIdx,other,otherIdx)</code>
/// returns the same as <code>other.equals(otherIdx,this,thisIdx)</code>.
/// </summary>
/// <param name="thisIdx">
/// Index within <code>this</code> sequence; must be in the range
/// <code>[ 0, this.size() )</code>.
/// </param>
/// <param name="other">
/// Another sequence; must be the same implementation class, that
/// is <code>this.getClass() == other.getClass()</code>. </param>
/// <param name="otherIdx">
/// Index within <code>other</code> sequence; must be in the range
/// <code>[ 0, other.size() )</code>. </param>
/// <returns>
/// true if the elements are equal; false if they are not equal.
/// </returns>
bool Equals(int thisIdx, ISequence other, int otherIdx);
}
}

594
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/MyersDiff.cs

@ -0,0 +1,594 @@ @@ -0,0 +1,594 @@
/*
* Copyright (C) 2008, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* Copyright (C) 2009, Gil Ran <gilrun@gmail.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Git Development Community nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.AvalonEdit.AddIn.MyersDiff
{
/// <summary>
/// Diff algorithm, based on "An O(ND) Difference Algorithm and its
/// Variations", by Eugene Myers.
///
/// The basic idea is to put the line numbers of text A as columns ("x") and the
/// lines of text B as rows ("y"). Now you try to find the shortest "edit path"
/// from the upper left corner to the lower right corner, where you can
/// always go horizontally or vertically, but diagonally from (x,y) to
/// (x+1,y+1) only if line x in text A is identical to line y in text B.
///
/// Myers' fundamental concept is the "furthest reaching D-path on diagonal k":
/// a D-path is an edit path starting at the upper left corner and containing
/// exactly D non-diagonal elements ("differences"). The furthest reaching
/// D-path on diagonal k is the one that contains the most (diagonal) elements
/// which ends on diagonal k (where k = y - x).
///
/// Example:
///
/// H E L L O W O R L D
/// ____
/// L \___
/// O \___
/// W \________
///
/// Since every D-path has exactly D horizontal or vertical elements, it can
/// only end on the diagonals -D, -D+2, ..., D-2, D.
///
/// Since every furthest reaching D-path contains at least one furthest
/// reaching (D-1)-path (except for D=0), we can construct them recursively.
///
/// Since we are really interested in the shortest edit path, we can start
/// looking for a 0-path, then a 1-path, and so on, until we find a path that
/// ends in the lower right corner.
///
/// To save space, we do not need to store all paths (which has quadratic space
/// requirements), but generate the D-paths simultaneously from both sides.
/// When the ends meet, we will have found "the middle" of the path. From the
/// end points of that diagonal part, we can generate the rest recursively.
///
/// This only requires linear space.
///
/// The overall (runtime) complexity is
///
/// O(N * D^2 + 2 * N/2 * (D/2)^2 + 4 * N/4 * (D/4)^2 + ...)
/// = O(N * D^2 * 5 / 4) = O(N * D^2),
///
/// (With each step, we have to find the middle parts of twice as many regions
/// as before, but the regions (as well as the D) are halved.)
///
/// So the overall runtime complexity stays the same with linear space,
/// albeit with a larger constant factor.
/// </summary>
public class MyersDiff
{
/// <summary>
/// The list of edits found during the last call to <see cref="calculateEdits()"/>
/// </summary>
protected List<Edit> edits;
/// <summary>
/// The first text to be compared. Referred to as "Text A" in the comments
/// </summary>
protected ISequence a;
/// <summary>
/// The second text to be compared. Referred to as "Text B" in the comments
/// </summary>
protected ISequence b;
/// <summary>
/// The only constructor
/// </summary>
/// <param name="a">the text A which should be compared</param>
/// <param name="b">the text B which should be compared</param>
public MyersDiff(ISequence a, ISequence b)
{
this.a = a;
this.b = b;
middle = new MiddleEdit(a, b);
CalculateEdits();
}
/// <returns>the list of edits found during the last call to {@link #calculateEdits()}</returns>
public List<Edit> GetEdits()
{
return edits;
}
// TODO: use ThreadLocal for future multi-threaded operations
MiddleEdit middle;
/// <summary>
/// Entrypoint into the algorithm this class is all about. This method triggers that the
/// differences between A and B are calculated in form of a list of edits.
/// </summary>
protected void CalculateEdits()
{
edits = new List<Edit>();
middle.Initialize(0, a.Size(), 0, b.Size());
if (middle.beginA >= middle.endA &&
middle.beginB >= middle.endB)
return;
CalculateEdits(middle.beginA, middle.endA,
middle.beginB, middle.endB);
}
/// <summary>
/// Calculates the differences between a given part of A against another given part of B
/// </summary>
/// <param name="beginA">start of the part of A which should be compared (0&lt;=beginA&lt;sizeof(A))</param>
/// <param name="endA">end of the part of A which should be compared (beginA&lt;=endA&lt;sizeof(A))</param>
/// <param name="beginB">start of the part of B which should be compared (0&lt;=beginB&lt;sizeof(B))</param>
/// <param name="endB">end of the part of B which should be compared (beginB&lt;=endB&lt;sizeof(B))</param>
protected void CalculateEdits(int beginA, int endA,
int beginB, int endB)
{
Edit edit = middle.Calculate(beginA, endA, beginB, endB);
if (beginA < edit.BeginA || beginB < edit.BeginB)
{
int k = edit.BeginB - edit.BeginA;
int x = middle.backward.Snake(k, edit.BeginA);
CalculateEdits(beginA, x, beginB, k + x);
}
if (edit.EditType != ChangeType.None)
edits.Add(edit);
// after middle
if (endA > edit.EndA || endB > edit.EndB)
{
int k = edit.EndB - edit.EndA;
int x = middle.forward.Snake(k, edit.EndA);
CalculateEdits(x, endA, k + x, endB);
}
}
/// <summary>
/// A class to help bisecting the sequences a and b to find minimal
/// edit paths.
///
/// As the arrays are reused for space efficiency, you will need one
/// instance per thread.
///
/// The entry function is the calculate() method.
/// </summary>
class MiddleEdit
{
private readonly ISequence _a;
private readonly ISequence _b;
public MiddleEdit(ISequence a, ISequence b)
{
_a = a;
_b = b;
forward = new ForwardEditPaths(this);
backward = new BackwardEditPaths(this);
}
public void Initialize(int beginA, int endA, int beginB, int endB)
{
this.beginA = beginA; this.endA = endA;
this.beginB = beginB; this.endB = endB;
// strip common parts on either end
int k = beginB - beginA;
this.beginA = forward.Snake(k, beginA);
this.beginB = k + this.beginA;
k = endB - endA;
this.endA = backward.Snake(k, endA);
this.endB = k + this.endA;
}
/// <summary>
/// This function calculates the "middle" Edit of the shortest
/// edit path between the given subsequences of a and b.
///
/// Once a forward path and a backward path meet, we found the
/// middle part. From the last snake end point on both of them,
/// we construct the Edit.
///
/// It is assumed that there is at least one edit in the range.
/// </summary>
// TODO: measure speed impact when this is synchronized
public Edit Calculate(int beginA, int endA, int beginB, int endB)
{
if (beginA == endA || beginB == endB)
return new Edit(beginA, endA, beginB, endB);
this.beginA = beginA; this.endA = endA;
this.beginB = beginB; this.endB = endB;
/*
* Following the conventions in Myers' paper, "k" is
* the difference between the index into "b" and the
* index into "a".
*/
int minK = beginB - endA;
int maxK = endB - beginA;
forward.Initialize(beginB - beginA, beginA, minK, maxK);
backward.Initialize(endB - endA, endA, minK, maxK);
for (int d = 1; ; d++)
if (forward.Calculate(d) ||
backward.Calculate(d))
{
return _edit;
}
}
/*
* For each d, we need to hold the d-paths for the diagonals
* k = -d, -d + 2, ..., d - 2, d. These are stored in the
* forward (and backward) array.
*
* As we allow subsequences, too, this needs some refinement:
* the forward paths start on the diagonal forwardK =
* beginB - beginA, and backward paths start on the diagonal
* backwardK = endB - endA.
*
* So, we need to hold the forward d-paths for the diagonals
* k = forwardK - d, forwardK - d + 2, ..., forwardK + d and
* the analogue for the backward d-paths. This means that
* we can turn (k, d) into the forward array index using this
* formula:
*
* i = (d + k - forwardK) / 2
*
* There is a further complication: the edit paths should not
* leave the specified subsequences, so k is bounded by
* minK = beginB - endA and maxK = endB - beginA. However,
* (k - forwardK) _must_ be odd whenever d is odd, and it
* _must_ be even when d is even.
*
* The values in the "forward" and "backward" arrays are
* positions ("x") in the sequence a, to get the corresponding
* positions ("y") in the sequence b, you have to calculate
* the appropriate k and then y:
*
* k = forwardK - d + i * 2
* y = k + x
*
* (substitute backwardK for forwardK if you want to get the
* y position for an entry in the "backward" array.
*/
public EditPaths forward;
public EditPaths backward;
/* Some variables which are shared between methods */
public int beginA;
public int endA;
public int beginB;
public int endB;
protected Edit _edit;
internal abstract class EditPaths
{
protected readonly MiddleEdit _middleEdit;
private List<int> x = new List<int>();
private List<long> _snake = new List<long>();
public int beginK;
public int endK;
public int middleK;
int prevBeginK, prevEndK;
/* if we hit one end early, no need to look further */
protected int minK, maxK; // TODO: better explanation
protected EditPaths(MiddleEdit middleEdit)
{
_middleEdit = middleEdit;
}
int GetIndex(int d, int k)
{
// TODO: remove
if (((d + k - middleK) % 2) == 1)
throw new InvalidOperationException("odd: " + d + " + " + k + " - " + middleK);
return (d + k - middleK) / 2;
}
public int GetX(int d, int k)
{
// TODO: remove
if (k < beginK || k > endK)
throw new InvalidOperationException("k " + k + " not in " + beginK + " - " + endK);
return x[GetIndex(d, k)];
}
public long GetSnake(int d, int k)
{
// TODO: remove
if (k < beginK || k > endK)
throw new InvalidOperationException("k " + k + " not in " + beginK + " - " + endK);
return _snake[GetIndex(d, k)];
}
private int ForceKIntoRange(int k)
{
/* if k is odd, so must be the result */
if (k < minK)
return minK + ((k ^ minK) & 1);
else if (k > maxK)
return maxK - ((k ^ maxK) & 1);
return k;
}
public void Initialize(int k, int x, int minK, int maxK)
{
this.minK = minK;
this.maxK = maxK;
beginK = endK = middleK = k;
this.x.Clear();
this.x.Add(x);
_snake.Clear();
_snake.Add(NewSnake(k, x));
}
public abstract int Snake(int k, int x);
protected abstract int GetLeft(int x);
protected abstract int GetRight(int x);
protected abstract bool IsBetter(int left, int right);
protected abstract void AdjustMinMaxK(int k, int x);
protected abstract bool Meets(int d, int k, int x, long snake);
long NewSnake(int k, int x)
{
long y = k + x;
long ret = ((long)x) << 32;
return ret | y;
}
int Snake2x(long snake)
{
return (int)((ulong)snake >> 32);
}
int Snake2y(long snake)
{
return (int)(((ulong)snake << 32) >> 32);
}
protected bool MakeEdit(long snake1, long snake2)
{
int x1 = Snake2x(snake1), x2 = Snake2x(snake2);
int y1 = Snake2y(snake1), y2 = Snake2y(snake2);
/*
* Check for incompatible partial edit paths:
* when there are ambiguities, we might have
* hit incompatible (i.e. non-overlapping)
* forward/backward paths.
*
* In that case, just pretend that we have
* an empty edit at the end of one snake; this
* will force a decision which path to take
* in the next recursion step.
*/
if (x1 > x2 || y1 > y2)
{
x1 = x2;
y1 = y2;
}
_middleEdit._edit = new Edit(x1, x2, y1, y2);
return true;
}
public bool Calculate(int d)
{
prevBeginK = beginK;
prevEndK = endK;
beginK = ForceKIntoRange(middleK - d);
endK = ForceKIntoRange(middleK + d);
// TODO: handle i more efficiently
// TODO: walk snake(k, getX(d, k)) only once per (d, k)
// TODO: move end points out of the loop to avoid conditionals inside the loop
// go backwards so that we can avoid temp vars
for (int k = endK; k >= beginK; k -= 2)
{
int left = -1, right = -1;
long leftSnake = -1L, rightSnake = -1L;
// TODO: refactor into its own function
int i;
if (k > prevBeginK)
{
i = GetIndex(d - 1, k - 1);
left = x[i];
int end = Snake(k - 1, left);
leftSnake = left != end ?
NewSnake(k - 1, end) :
_snake[i];
if (Meets(d, k - 1, end, leftSnake))
return true;
left = GetLeft(end);
}
if (k < prevEndK)
{
i = GetIndex(d - 1, k + 1);
right = x[i];
int end = Snake(k + 1, right);
rightSnake = right != end ?
NewSnake(k + 1, end) :
_snake[i];
if (Meets(d, k + 1, end, rightSnake))
return true;
right = GetRight(end);
}
int newX;
long newSnakeTmp;
if (k >= prevEndK ||
(k > prevBeginK &&
IsBetter(left, right)))
{
newX = left;
newSnakeTmp = leftSnake;
}
else
{
newX = right;
newSnakeTmp = rightSnake;
}
if (Meets(d, k, newX, newSnakeTmp))
return true;
AdjustMinMaxK(k, newX);
i = GetIndex(d, k);
x.Set(i, newX);
_snake.Set(i, newSnakeTmp);
}
return false;
}
}
class ForwardEditPaths : EditPaths
{
public ForwardEditPaths(MiddleEdit middleEdit)
: base(middleEdit)
{
}
public override int Snake(int k, int x)
{
for (; x < _middleEdit.endA && k + x < _middleEdit.endB; x++)
if (!_middleEdit._a.Equals(x, _middleEdit._b, k + x))
break;
return x;
}
protected override int GetLeft(int x)
{
return x;
}
protected override int GetRight(int x)
{
return x + 1;
}
protected override bool IsBetter(int left, int right)
{
return left > right;
}
protected override void AdjustMinMaxK(int k, int x)
{
if (x >= _middleEdit.endA || k + x >= _middleEdit.endB)
{
if (k > _middleEdit.backward.middleK)
maxK = k;
else
minK = k;
}
}
protected override bool Meets(int d, int k, int x, long snake)
{
if (k < _middleEdit.backward.beginK || k > _middleEdit.backward.endK)
return false;
// TODO: move out of loop
if (((d - 1 + k - _middleEdit.backward.middleK) % 2) == 1)
return false;
if (x < _middleEdit.backward.GetX(d - 1, k))
return false;
MakeEdit(snake, _middleEdit.backward.GetSnake(d - 1, k));
return true;
}
}
class BackwardEditPaths : EditPaths
{
public BackwardEditPaths(MiddleEdit middleEdit)
: base(middleEdit)
{
}
public override int Snake(int k, int x)
{
for (; x > _middleEdit.beginA && k + x > _middleEdit.beginB; x--)
if (!_middleEdit._a.Equals(x - 1, _middleEdit._b, k + x - 1))
break;
return x;
}
protected override int GetLeft(int x)
{
return x - 1;
}
protected override int GetRight(int x)
{
return x;
}
protected override bool IsBetter(int left, int right)
{
return left < right;
}
protected override void AdjustMinMaxK(int k, int x)
{
if (x <= _middleEdit.beginA || k + x <= _middleEdit.beginB)
{
if (k > _middleEdit.forward.middleK)
maxK = k;
else
minK = k;
}
}
protected override bool Meets(int d, int k, int x, long snake)
{
if (k < _middleEdit.forward.beginK || k > _middleEdit.forward.endK)
return false;
// TODO: move out of loop
if (((d + k - _middleEdit.forward.middleK) % 2) == 1)
return false;
if (x > _middleEdit.forward.GetX(d, k))
return false;
MakeEdit(_middleEdit.forward.GetSnake(d, k), snake);
return true;
}
}
}
}
}

34
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/StringSequence.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.AvalonEdit.AddIn.MyersDiff
{
public class StringSequence : ISequence
{
string content;
public StringSequence(string content)
{
this.content = content;
}
public int Size()
{
return content.Length;
}
public bool Equals(int i, ISequence other, int j)
{
StringSequence seq = other as StringSequence;
if (seq == null)
return false;
return content[i] == seq.content[j];
}
}
}

22
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/MyersDiff/Utils.cs

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
namespace ICSharpCode.AvalonEdit.AddIn.MyersDiff
{
public static class Utils
{
public static void Set<T>(this IList<T> instance, int index, T value)
{
if (instance == null)
throw new ArgumentNullException("instance");
if (index == instance.Count)
instance.Add(value);
else
instance[index] = value;
}
}
}

5
src/AddIns/VersionControl/GitAddIn/GitAddIn.addin

@ -18,6 +18,11 @@ @@ -18,6 +18,11 @@
class = "ICSharpCode.GitAddIn.RegisterEventsCommand"/>
</Path>
<Path name="/Workspace/DocumentVersionProviders">
<Class id="git"
class="ICSharpCode.GitAddIn.GitVersionProvider" />
</Path>
<Path name = "/SharpDevelop/Pads/ProjectBrowser/ContextMenu/GitItem">
<Condition name = "IsUnderGitControl">
<MenuItem id = "GitSeparator" type = "Separator"/>

1
src/AddIns/VersionControl/GitAddIn/GitAddIn.csproj

@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
<Compile Include="Src\GitGuiWrapper.cs" />
<Compile Include="Src\GitMessageView.cs" />
<Compile Include="Src\GitStatusCache.cs" />
<Compile Include="Src\GitVersionProvider.cs" />
<Compile Include="Src\IsUnderGitControlCondition.cs" />
<Compile Include="Src\OverlayIconManager.cs" />
<Compile Include="Src\RegisterEventsCommand.cs" />

128
src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Util;
using Microsoft.Win32.SafeHandles;
namespace ICSharpCode.GitAddIn
{
public class GitVersionProvider : IDocumentVersionProvider
{
#region PInvoke
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref StartupInfo lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
const uint STARTF_USESTDHANDLES = 0x00000100;
const int STD_INPUT_HANDLE = -10;
const int STD_OUTPUT_HANDLE = -11;
const int STD_ERROR_HANDLE = -12;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct StartupInfo
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public SafePipeHandle hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
#endregion
public Stream OpenBaseVersion(string fileName)
{
if (!Git.IsInWorkingCopy(fileName))
return null;
return OpenOutput(fileName, GetBlobHash(fileName));
}
string GetBlobHash(string fileName)
{
ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(fileName);
runner.Start("cmd", "/c git ls-tree HEAD " + Path.GetFileName(fileName));
runner.WaitForExit();
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();
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdOutput = pipe.ClientSafePipeHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startupInfo.cb = 16;
PROCESS_INFORMATION procInfo;
if (!CreateProcess(null, string.Format("cmd /c git cat-file blob {0}", blobHash),
IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref startupInfo,
out procInfo)) {
pipe.DisposeLocalCopyOfClientHandle();
pipe.Close();
return null;
}
pipe.DisposeLocalCopyOfClientHandle();
return pipe;
}
}
}

5
src/AddIns/VersionControl/SubversionAddIn/ICSharpCode.Svn.addin

@ -23,6 +23,11 @@ @@ -23,6 +23,11 @@
class = "ICSharpCode.Svn.Commands.RegisterEventsCommand"/>
</Path>
<Path name="/Workspace/DocumentVersionProviders">
<Class id="svn"
class="ICSharpCode.Svn.SvnVersionProvider" />
</Path>
<Path name = "/SharpDevelop/Workbench/DisplayBindings">
<DisplayBinding id = "SubversionHistoryView"
type = "Secondary"

8
src/AddIns/VersionControl/SubversionAddIn/Src/Gui/HistoryViewDisplayBinding/HistoryViewDisplayBinding.cs

@ -34,12 +34,8 @@ namespace ICSharpCode.Svn @@ -34,12 +34,8 @@ namespace ICSharpCode.Svn
if (file == null || file.IsUntitled || !File.Exists(file.FileName)) {
return false;
}
if (Commands.RegisterEventsCommand.CanBeVersionControlledFile(file.FileName)) {
StatusKind status = OverlayIconManager.GetStatus(file.FileName);
return status != StatusKind.None && status != StatusKind.Unversioned && status != StatusKind.Ignored;
} else {
return false;
}
return SvnClientWrapper.IsInSourceControl(file.FileName);
}
}
}

28
src/AddIns/VersionControl/SubversionAddIn/Src/SvnClientWrapper.cs

@ -443,6 +443,34 @@ namespace ICSharpCode.Svn @@ -443,6 +443,34 @@ namespace ICSharpCode.Svn
}
}
public Stream OpenBaseVersion(string fileName)
{
MemoryStream stream = new MemoryStream();
if (!this.client.Write(fileName, stream, new SvnWriteArgs() { Revision = SvnRevision.Base }))
return null;
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
public Stream OpenCurrentVersion(string fileName)
{
MemoryStream stream = new MemoryStream();
if (!this.client.Write(fileName, stream, new SvnWriteArgs() { Revision = SvnRevision.Working }))
return null;
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
public static bool IsInSourceControl(string fileName)
{
if (Commands.RegisterEventsCommand.CanBeVersionControlledFile(fileName)) {
StatusKind status = OverlayIconManager.GetStatus(fileName);
return status != StatusKind.None && status != StatusKind.Unversioned && status != StatusKind.Ignored;
} else {
return false;
}
}
static StatusKind ToStatusKind(SvnStatus kind)
{
switch (kind) {

23
src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.Svn.Commands;
using SharpSvn;
namespace ICSharpCode.Svn
{
public class SvnVersionProvider : IDocumentVersionProvider
{
public Stream OpenBaseVersion(string fileName)
{
if (!SvnClientWrapper.IsInSourceControl(fileName))
return null;
using (SvnClientWrapper client = new SvnClientWrapper())
return client.OpenBaseVersion(fileName);
}
}
}

1
src/AddIns/VersionControl/SubversionAddIn/SubversionAddIn.csproj

@ -85,6 +85,7 @@ @@ -85,6 +85,7 @@
<Compile Include="Src\Commands\CheckoutCommands.cs" />
<Compile Include="Src\SubversionStateCondition.cs" />
<Compile Include="Src\SvnClientWrapper.cs" />
<Compile Include="Src\SvnVersionProvider.cs" />
<Compile Include="Src\SvnMessageView.cs" />
<EmbeddedResource Include="Src\Gui\HistoryViewDisplayBinding\InfoPanel.resx">
<DependentUpon>InfoPanel.cs</DependentUpon>

20
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/CompressingTreeList.cs

@ -353,6 +353,26 @@ namespace ICSharpCode.AvalonEdit.Utils @@ -353,6 +353,26 @@ namespace ICSharpCode.AvalonEdit.Utils
return GetNode(ref index).count - index;
}
/// <summary>
/// Applies the conversion function to all elements in this CompressingTreeList.
/// </summary>
public void Transform(Func<T, T> 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;
}
}
/// <summary>
/// Inserts the specified <paramref name="item"/> at <paramref name="index"/>
/// </summary>

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -119,6 +119,7 @@ @@ -119,6 +119,7 @@
<Compile Include="Src\Editor\Commands\FindReferences.cs" />
<Compile Include="Src\Editor\Commands\GotoLineNumber.cs" />
<Compile Include="Src\Editor\Commands\SymbolUnderCaretMenuCommand.cs" />
<Compile Include="Src\Editor\IDocumentBaseVersionProvider.cs" />
<Compile Include="Src\Editor\IEditorControlService.cs" />
<Compile Include="Src\Editor\IEditorUIService.cs" />
<Compile Include="Src\Editor\CodeCompletion\AttributesItemProvider.cs" />

122
src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Editor
{
public interface IDocumentVersionProvider
{
/// <summary>
/// Provides the BASE-Version for a file. This can be either the file saved
/// to disk or a base version provided by any VCS.
/// </summary>
Stream OpenBaseVersion(string fileName);
}
public sealed class DefaultVersionProvider : IDocumentVersionProvider
{
public Stream OpenBaseVersion(string fileName)
{
return File.OpenRead(fileName);
}
}
public class VersioningServices
{
public static readonly VersioningServices Instance = new VersioningServices();
List<IDocumentVersionProvider> baseVersionProviders;
public List<IDocumentVersionProvider> DocumentVersionProviders {
get {
if (baseVersionProviders == null)
baseVersionProviders = AddInTree.BuildItems<IDocumentVersionProvider>("/Workspace/DocumentVersionProviders", this, false);
return baseVersionProviders;
}
}
}
public interface IChangeWatcher : IDisposable
{
event EventHandler ChangeOccurred;
/// <summary>
/// Returns the change information for a given line.
/// Pass null to get the changes before the first line.
/// </summary>
LineChangeInfo GetChange(IDocumentLine line);
void Initialize(IDocument document);
}
public enum ChangeType
{
None,
Added,
Deleted,
Modified,
Unsaved
}
public struct LineChangeInfo : IEquatable<LineChangeInfo>
{
public static readonly LineChangeInfo Empty = new LineChangeInfo(ChangeType.None, "");
ChangeType change;
public ChangeType Change {
get { return change; }
set { change = value; }
}
string deletedLinesAfterThisLine;
public string DeletedLinesAfterThisLine {
get { return deletedLinesAfterThisLine; }
set { deletedLinesAfterThisLine = value; }
}
public LineChangeInfo(ChangeType change, string deletedLinesAfterThisLine)
{
this.change = change;
this.deletedLinesAfterThisLine = deletedLinesAfterThisLine;
}
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
return (obj is LineChangeInfo) && Equals((LineChangeInfo)obj);
}
public bool Equals(LineChangeInfo other)
{
return this.change == other.change && this.deletedLinesAfterThisLine == other.deletedLinesAfterThisLine;
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked {
hashCode += 1000000007 * change.GetHashCode();
if (deletedLinesAfterThisLine != null)
hashCode += 1000000009 * deletedLinesAfterThisLine.GetHashCode();
}
return hashCode;
}
public static bool operator ==(LineChangeInfo lhs, LineChangeInfo rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(LineChangeInfo lhs, LineChangeInfo rhs)
{
return !(lhs == rhs);
}
#endregion
}
}
Loading…
Cancel
Save