[Monodevelop-patches-list] r2604 - in trunk/MonoDevelop: . Extras Extras/VersionControl Extras/VersionControl/AddIn Extras/VersionControl/Diff Extras/VersionControl/DiffWidget Extras/VersionControl/VersionControl
Joshua Tauberer
joshua at mono-cvs.ximian.com
Wed Jun 22 15:12:34 EDT 2005
Author: joshua
Date: 2005-06-22 15:12:34 -0400 (Wed, 22 Jun 2005)
New Revision: 2604
Added:
trunk/MonoDevelop/Extras/VersionControl/
trunk/MonoDevelop/Extras/VersionControl/AddIn/
trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs
trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs
trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs
trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am
trunk/MonoDevelop/Extras/VersionControl/AddIn/README
trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs
trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs
trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs
trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml
trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_added.png
trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_conflicted.png
trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_locked.png
trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_modified.png
trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_normal.png
trunk/MonoDevelop/Extras/VersionControl/ChangeLog
trunk/MonoDevelop/Extras/VersionControl/Diff/
trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs
trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am
trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs
trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs
trunk/MonoDevelop/Extras/VersionControl/Diff/README
trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs
trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs
trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs
trunk/MonoDevelop/Extras/VersionControl/DiffWidget/
trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am
trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README
trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs
trunk/MonoDevelop/Extras/VersionControl/Makefile.am
trunk/MonoDevelop/Extras/VersionControl/README
trunk/MonoDevelop/Extras/VersionControl/VersionControl/
trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am
trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs
trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs
Modified:
trunk/MonoDevelop/ChangeLog
trunk/MonoDevelop/configure.in
Log:
Checked in VersionControl files.
Modified: trunk/MonoDevelop/ChangeLog
===================================================================
--- trunk/MonoDevelop/ChangeLog 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/ChangeLog 2005-06-22 19:12:34 UTC (rev 2604)
@@ -1,3 +1,8 @@
+2005-06-22 Joshua Tauberer <tauberer at for.net>
+ * configure.in: Added Extras/VersionControl automake files to the
+ build.
+ * Extras/VersionControl: Checked in VersionControl addin files.
+
2005-06-08 Todd Berman <tberman at off.net>
* monodevelop.in: Remove bashism, patch from meebey (mail at meebey.net)
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,244 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+
+using MonoDevelop.Gui;
+using MonoDevelop.Core;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Commands;
+using MonoDevelop.Services;
+
+using VersionControl;
+
+namespace VersionControlPlugin {
+
+ public class VersionControlService {
+
+ public static ArrayList Providers = new ArrayList();
+
+ static Gdk.Pixbuf overlay_normal = Gdk.Pixbuf.LoadFromResource("overlay_normal.png");
+ static Gdk.Pixbuf overlay_modified = Gdk.Pixbuf.LoadFromResource("overlay_modified.png");
+ static Gdk.Pixbuf overlay_conflicted = Gdk.Pixbuf.LoadFromResource("overlay_conflicted.png");
+ static Gdk.Pixbuf overlay_added = Gdk.Pixbuf.LoadFromResource("overlay_added.png");
+
+ public static Gdk.Pixbuf LoadIconForStatus(NodeStatus status) {
+ if (status == NodeStatus.Unchanged)
+ return overlay_normal;
+ if (status == NodeStatus.Modified)
+ return overlay_modified;
+ if (status == NodeStatus.Conflicted)
+ return overlay_conflicted;
+ if (status == NodeStatus.ScheduledAdd)
+ return overlay_added;
+ return null;
+ }
+
+ static VersionControlService() {
+ Providers.Add(new SubversionVersionControl());
+ }
+ }
+
+ public class VersionControlNodeExtension : NodeBuilderExtension {
+ public override bool CanBuildNode (Type dataType) {
+ //Console.Error.WriteLine(dataType);
+ return typeof(ProjectFile).IsAssignableFrom (dataType)
+ || typeof(DotNetProject).IsAssignableFrom (dataType);
+ // TODO: Folders
+ }
+
+ public override void BuildNode (ITreeBuilder builder, object dataObject, ref string label, ref Gdk.Pixbuf icon, ref Gdk.Pixbuf closedIcon) {
+ // Add status overlays
+
+ // TODO: Watch the files in some way to detect
+ // when the overlay should be changed.
+
+ if (!(dataObject is ProjectFile)) return;
+ if (!builder.Options["ShowVersionControlOverlays"])
+ return;
+
+ ProjectFile file = (ProjectFile) dataObject;
+ try {
+ foreach (VersionControlSystem vc in VersionControlService.Providers) {
+ if (vc.IsFileStatusAvailable(file.FilePath)) {
+ Node node = vc.GetFileStatus(file.FilePath, false);
+
+ Gdk.Pixbuf overlay = VersionControlService.LoadIconForStatus(node.Status);
+
+ double scale = (double)(2*icon.Width/3) / (double)overlay.Width;
+ int w = (int)(overlay.Width*scale);
+ int h = (int)(overlay.Height*scale);
+ icon = icon.Copy();
+ overlay.Composite(icon,
+ icon.Width-w, icon.Height-h,
+ w, h,
+ icon.Width-w, icon.Height-h,
+ scale,scale, Gdk.InterpType.Bilinear, 255);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ Console.Error.WriteLine(e);
+ }
+ }
+
+ public override Type CommandHandlerType {
+ get { return typeof(AddinCommandHandler); }
+ }
+ }
+
+ public enum Commands {
+ Update,
+ Diff,
+ Log,
+ Status
+ }
+
+ class AddinCommandHandler : NodeCommandHandler {
+ [CommandHandler (Commands.Update)]
+ protected void OnUpdate() {
+ RunCommand(Commands.Update, false);
+ }
+
+ [CommandUpdateHandler (Commands.Update)]
+ protected void UpdateUpdate(CommandInfo item) {
+ TestCommand(Commands.Update, item);
+ }
+
+ [CommandHandler (Commands.Diff)]
+ protected void OnDiff() {
+ RunCommand(Commands.Diff, false);
+ }
+
+ [CommandUpdateHandler (Commands.Diff)]
+ protected void UpdateDiff(CommandInfo item) {
+ TestCommand(Commands.Diff, item);
+ }
+
+ [CommandHandler (Commands.Log)]
+ protected void OnLog() {
+ RunCommand(Commands.Log, false);
+ }
+
+ [CommandUpdateHandler (Commands.Log)]
+ protected void UpdateLog(CommandInfo item) {
+ TestCommand(Commands.Log, item);
+ }
+
+ [CommandHandler (Commands.Status)]
+ protected void OnStatus() {
+ RunCommand(Commands.Status, false);
+ }
+
+ [CommandUpdateHandler (Commands.Status)]
+ protected void UpdateStatus(CommandInfo item) {
+ TestCommand(Commands.Status, item);
+ }
+
+ private void TestCommand(Commands cmd, CommandInfo item) {
+ item.Visible = RunCommand(cmd, true);
+ }
+
+ private bool RunCommand(Commands cmd, bool test) {
+ string path;
+ bool isDir;
+
+ if (CurrentNode.DataItem is ProjectFile) {
+ ProjectFile file = (ProjectFile)CurrentNode.DataItem;
+ path = file.FilePath;
+ isDir = false;
+ } else if (CurrentNode.DataItem is DotNetProject) {
+ DotNetProject project = (DotNetProject)CurrentNode.DataItem;
+ path = project.BaseDirectory;
+ isDir = true;
+ } else {
+ Console.Error.WriteLine(CurrentNode.DataItem);
+ return false;
+ }
+
+ switch (cmd) {
+ case Commands.Update:
+ return UpdateCommand.Update(path, test);
+ case Commands.Diff:
+ return DiffView.Show(path, test);
+ case Commands.Log:
+ return LogView.Show(path, isDir, null, test);
+ case Commands.Status:
+ return StatusView.Show(path, test);
+ }
+ return false;
+ }
+ }
+
+ public abstract class BaseView : AbstractBaseViewContent, IViewContent {
+ string name;
+ public BaseView(string name) { this.name = name; }
+
+ protected virtual void SaveAs(string fileName) {
+ }
+
+ void IViewContent.Load(string fileName) {
+ throw new InvalidOperationException();
+ }
+ void IViewContent.Save() {
+ throw new InvalidOperationException();
+ }
+ void IViewContent.Save(string fileName) {
+ SaveAs(fileName);
+ }
+
+ string IViewContent.ContentName {
+ get { return name; }
+ set { }
+ }
+
+ bool IViewContent.HasProject {
+ get { return false; }
+ set { }
+ }
+
+ bool IViewContent.IsDirty {
+ get { return false; }
+ set { }
+ }
+
+ bool IViewContent.IsReadOnly {
+ get { return true; }
+ }
+
+ bool IViewContent.IsUntitled {
+ get { return false; }
+ }
+
+ bool IViewContent.IsViewOnly {
+ get { return false; }
+ }
+
+ string IViewContent.PathRelativeToProject {
+ get { return ""; }
+ }
+
+ MonoDevelop.Internal.Project.Project IViewContent.Project {
+ get { return null; }
+ set { }
+ }
+
+ string IViewContent.TabPageLabel {
+ get { return name; }
+ }
+
+ string IViewContent.UntitledName {
+ get { return ""; }
+ set { }
+ }
+
+ event EventHandler IViewContent.BeforeSave { add { } remove { } }
+ event EventHandler IViewContent.ContentChanged { add { } remove { } }
+ event EventHandler IViewContent.ContentNameChanged { add { } remove { } }
+ event EventHandler IViewContent.DirtyChanged { add { } remove { } }
+ }
+
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,141 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+
+using VersionControl;
+
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Core.AddIns.Conditions;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Dialogs;
+
+using Algorithm.Diff.Gtk;
+
+namespace VersionControlPlugin {
+ public class DiffView : BaseView {
+ object left, right;
+ Algorithm.Diff.Diff diff;
+ HBox box = new HBox(true, 0);
+ DiffWidget widget;
+ ThreadNotify threadnotify;
+
+ System.IO.FileSystemWatcher leftwatcher, rightwatcher;
+
+ double pos = -1;
+
+ public static bool Show(string filepath, bool test) {
+ foreach (VersionControlSystem vc in VersionControlService.Providers) {
+ if (vc.IsDiffAvailable(filepath)) {
+ if (test) return true;
+ DiffView d = new DiffView(
+ Path.GetFileName(filepath),
+ vc.GetPathToBaseText(filepath),
+ filepath);
+ MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static string[] split(string text) {
+ if (text == "") return new string[0];
+ return text.Split('\n', '\r');
+ }
+
+ public static void Show(string name, string lefttext, string righttext) {
+ DiffView d = new DiffView(name, split(lefttext), split(righttext));
+ MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+ }
+
+ public DiffView(string name, string left, string right)
+ : base(name + " Changes") {
+ this.left = left;
+ this.right = right;
+
+ Refresh();
+
+ threadnotify = new ThreadNotify(new ReadyEvent(Refresh));
+
+ leftwatcher = new System.IO.FileSystemWatcher(Path.GetDirectoryName(left), Path.GetFileName(left));
+ rightwatcher = new System.IO.FileSystemWatcher(Path.GetDirectoryName(right), Path.GetFileName(right));
+
+ leftwatcher.Changed += new FileSystemEventHandler(filechanged);
+ rightwatcher.Changed += new FileSystemEventHandler(filechanged);
+
+ leftwatcher.EnableRaisingEvents = true;
+ rightwatcher.EnableRaisingEvents = true;
+ }
+
+ public DiffView(string name, string[] left, string[] right)
+ : base(name + " Changes") {
+ this.left = left;
+ this.right = right;
+
+ Refresh();
+ }
+
+ void filechanged(object src, FileSystemEventArgs args) {
+ threadnotify.WakeupMain();
+ }
+
+ private void Refresh() {
+ box.Show();
+
+ try {
+ if (left is string)
+ diff = new Algorithm.Diff.Diff((string)left, (string)right, false, true);
+ else if (left is string[])
+ diff = new Algorithm.Diff.Diff((string[])left, (string[])right, null, null);
+ } catch (Exception e) {
+ Console.Error.WriteLine(e.ToString());
+ return;
+ }
+
+ if (widget != null) {
+ pos = widget.Position;
+ box.Remove(widget);
+ widget.Dispose();
+ }
+
+ DiffWidget.Options opts = new DiffWidget.Options();
+ opts.Font = MonoDevelop.EditorBindings.Properties.TextEditorProperties.Font.ToString();
+ opts.LeftName = "Repository";
+ opts.RightName = "Working Copy";
+ widget = new DiffWidget(diff, opts);
+
+ box.Add(widget);
+ box.ShowAll();
+
+ widget.ExposeEvent += new ExposeEventHandler(OnExposed);
+ }
+
+ void OnExposed (object o, ExposeEventArgs args) {
+ if (pos != -1)
+ widget.Position = pos;
+ pos = -1;
+ }
+
+ protected override void SaveAs(string fileName) {
+ if (!(left is string)) return;
+
+ using (StreamWriter writer = new StreamWriter(fileName)) {
+ Algorithm.Diff.UnifiedDiff.WriteUnifiedDiff(
+ diff,
+ writer,
+ Path.GetFileName((string)right) + " (repository)",
+ Path.GetFileName((string)right) + " (working copy)",
+ 3);
+ }
+ }
+
+ public override Gtk.Widget Control {
+ get {
+ return box;
+ }
+ }
+ }
+}
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,224 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+using VersionControl;
+
+using MonoDevelop.Gui.Widgets;
+using MonoDevelop.SourceEditor.Gui;
+
+namespace VersionControlPlugin {
+ public class LogView : BaseView {
+ string filepath;
+ Widget widget;
+ Hashtable buttons = new Hashtable();
+ VersionControlSystem vc;
+ RevisionPtr since;
+
+ private class RevItem {
+ public RevisionPtr Rev;
+ public object Path;
+ }
+
+ public static bool Show(string filepath, bool isDirectory, RevisionPtr since, bool test) {
+ foreach (VersionControlSystem vc in VersionControlService.Providers) {
+ if (vc.IsHistoryAvailable(filepath)) {
+ if (test) return true;
+ new Worker(vc, filepath, isDirectory, since).Start();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class Worker : Task {
+ VersionControlSystem vc;
+ string filepath;
+ bool isDirectory;
+ RevisionPtr since;
+ RevisionDescription[] history;
+
+ public Worker(VersionControlSystem vc, string filepath, bool isDirectory, RevisionPtr since) {
+ this.vc = vc;
+ this.filepath = filepath;
+ this.isDirectory = isDirectory;
+ this.since = since;
+ }
+
+ protected override string GetDescription() {
+ return "Retrieving history for " + Path.GetFileName(filepath) + "...";
+ }
+
+ protected override void Run() {
+ history = vc.GetHistory(filepath, since);
+ }
+
+ protected override void Finished() {
+ LogView d = new LogView(filepath, isDirectory, history, vc);
+ MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+ }
+ }
+
+ public LogView(string filepath, bool isDirectory, RevisionDescription[] history, VersionControlSystem vc)
+ : base(Path.GetFileName(filepath) + " Log") {
+ this.vc = vc;
+ this.filepath = filepath;
+
+ ScrolledWindow scroller = new ScrolledWindow();
+ Viewport viewport = new Viewport();
+ VBox box = new VBox(false, 5);
+
+ viewport.Add(box);
+ scroller.Add(viewport);
+ widget = scroller;
+
+ foreach (RevisionDescription d in history) {
+ RevItem revitem = new RevItem();
+ revitem.Path = d.RepositoryPath;
+ revitem.Rev = d.Revision;
+
+ VBox item = new VBox(false, 1);
+
+ HBox header_row = new HBox(false, 2);
+ item.PackStart(header_row, false, false, 0);
+
+ Label header = new Label(d.Revision + " -- " + d.Time + " -- " + d.Author);
+ header.Xalign = 0;
+ header_row.Add(header);
+
+ if (!isDirectory) {
+ Button viewdiff = new Button("View Changes");
+ viewdiff.Clicked += new EventHandler(DiffButtonClicked);
+ header_row.Add(viewdiff);
+ buttons[viewdiff] = revitem;
+
+ Button viewtext = new Button("View File");
+ viewtext.Clicked += new EventHandler(ViewTextButtonClicked);
+ header_row.Add(viewtext);
+ buttons[viewtext] = revitem;
+ }
+
+ TextView message = new TextView();
+ message.Editable = false;
+ message.WrapMode = Gtk.WrapMode.WordChar;
+ message.Buffer.Text = d.Message == "" ? "No message." : d.Message;
+ item.PackStart(message, false, false, 0);
+
+ box.PackStart(item, false, false, 0);
+ }
+ }
+
+ void DiffButtonClicked(object src, EventArgs args) {
+ RevItem item = (RevItem)buttons[src];
+ new DiffWorker(Path.GetFileName(filepath), vc, item.Path, item.Rev).Start();
+ }
+
+ void ViewTextButtonClicked(object src, EventArgs args) {
+ RevItem item = (RevItem)buttons[src];
+ HistoricalFileView.Show(filepath, vc, item.Path, item.Rev);
+ }
+
+ public override Gtk.Widget Control {
+ get {
+ return widget;
+ }
+ }
+
+ internal class DiffWorker : Task {
+ VersionControlSystem vc;
+ string name;
+ object revPath;
+ RevisionPtr revision;
+ string text1, text2;
+
+ public DiffWorker(string name, VersionControlSystem vc, object revPath, RevisionPtr revision) {
+ this.name = name;
+ this.vc = vc;
+ this.revPath = revPath;
+ this.revision = revision;
+ }
+
+ protected override string GetDescription() {
+ return "Retreiving changes in " + name + " at " + revision + "...";
+ }
+
+ protected override void Run() {
+ Log("Getting text of " + revPath + " at " + revision.GetPrevious() + "...");
+ try {
+ text1 = vc.GetTextAtRevision(revPath, revision.GetPrevious());
+ } catch (Exception e) {
+ // If the file was added in this revision, no previous
+ // text exists.
+ text1 = "";
+ }
+ Log("Getting text of " + revPath + " at " + revision + "...");
+ text2 = vc.GetTextAtRevision(revPath, revision);
+ }
+
+ protected override void Finished() {
+ DiffView.Show(name + " " + revision.ToString(), text1, text2);
+ }
+ }
+
+ }
+
+ public class HistoricalFileView : BaseView {
+ SourceEditor widget;
+
+ public static void Show(string name, string file, string text) {
+ HistoricalFileView d = new HistoricalFileView(name, file, text);
+ MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+ }
+
+ public static void Show(string file, VersionControlSystem vc, object revPath, RevisionPtr revision) {
+ new Worker(Path.GetFileName(file) + " " + revision.ToString(),
+ file, vc, revPath, revision).Start();
+ }
+
+
+ public HistoricalFileView(string name, string file, string text)
+ : base(name) {
+
+ // How do I get it to recognize the language of the file?
+ widget = new SourceEditor(null);
+ widget.Text = text;
+ widget.View.Editable = false;
+ }
+
+ public override Gtk.Widget Control {
+ get {
+ return widget;
+ }
+ }
+
+ internal class Worker : Task {
+ VersionControlSystem vc;
+ string name, file;
+ object revPath;
+ RevisionPtr revision;
+ string text;
+
+ public Worker(string name, string file, VersionControlSystem vc, object revPath, RevisionPtr revision) {
+ this.name = name;
+ this.file = file;
+ this.vc = vc;
+ this.revPath = revPath;
+ this.revision = revision;
+ }
+
+ protected override string GetDescription() {
+ return "Retreiving content of " + name + " at " + revision + "...";
+ }
+
+ protected override void Run() {
+ text = vc.GetTextAtRevision(revPath, revision);
+ }
+
+ protected override void Finished() {
+ HistoricalFileView.Show(name, file, text);
+ }
+ }
+ }
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,42 @@
+FILES = *.cs
+
+REFS = /r:$(ADDIN_BUILD)/Diff.dll \
+ /r:$(ADDIN_BUILD)/DiffWidget.dll \
+ /r:$(ADDIN_BUILD)/VersionControl.dll \
+ /r:$(top_builddir)/build/bin/MonoDevelop.Core.dll \
+ /r:$(top_builddir)/build/bin/MonoDevelop.Base.dll \
+ /r:$(top_builddir)/build/bin/MonoDevelop.Gui.Widgets.dll \
+ /r:$(top_builddir)/build/bin/MonoDevelop.SourceEditor.dll \
+ /r:System.Data.dll \
+ $(GTK_SHARP_LIBS) \
+ $(GLADE_SHARP_LIBS) \
+ $(GCONF_SHARP_LIBS) \
+ $(GTKSOURCEVIEW_SHARP_LIBS)
+
+RES = -resource:overlay_added.png \
+ -resource:overlay_modified.png \
+ -resource:overlay_conflicted.png \
+ -resource:overlay_normal.png \
+ -resource:overlay_locked.png
+
+ADDIN = VersionControl.addin.xml
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+
+DLL = $(ADDIN_BUILD)/VersionControlAddIn.dll
+
+all: $(ADDIN_BUILD)/$(ADDIN) $(DLL)
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+ mkdir -p $(ADDIN_BUILD)
+ cp $(srcdir)/$(ADDIN) $(ADDIN_BUILD)/$(ADDIN)
+
+$(DLL): $(FILES)
+ mkdir -p $(ADDIN_BUILD)
+ $(CSC) $(CSC_FLAGS) -target:library -out:$@ $(FILES) $(REFS) $(RES)
+
+CLEANFILES = $(DLL) $(DLL).mdb $(ADDIN_BUILD)/$(ADDIN)
+
+EXTRA_DIST = $(FILES) $(ADDIN)
+
+
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/README 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/README 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,6 @@
+This is the actual addin for version control
+support in MD.
+
+The overlay_*.png icons come from the Tortoise
+project (http://tortoisesvn.tigris.org), which
+is a GPL project.
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,361 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+using VersionControl;
+
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Core.AddIns.Conditions;
+using MonoDevelop.Core.Services;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Dialogs;
+using MonoDevelop.SourceEditor.Gui;
+using MonoDevelop.Services;
+
+namespace VersionControlPlugin {
+ public class StatusView : BaseView {
+ string filepath;
+ VersionControlSystem vc;
+
+ Widget widget;
+ HBox commandbar;
+ VBox box, main;
+ Label status;
+ Table table;
+ Button showRemoteStatus;
+ Button buttonCommit;
+
+ bool commitShown = false;
+ Hashtable checkCommit;
+ VBox boxCommit;
+ TextView textCommitMessage;
+ Button buttonCommitCancel;
+ Button buttonCommitCommit;
+
+ Node[] statuses;
+
+ bool remoteStatus = false;
+
+ Hashtable buttonsShowLog;
+ Hashtable buttonsShowDiff;
+
+ private class RevItem {
+ public RevisionPtr BaseRev;
+ public string Path;
+ }
+
+ public static bool Show(string path, bool test) {
+ foreach (VersionControlSystem vc in VersionControlService.Providers) {
+ if (vc.IsDirectoryStatusAvailable(path)) {
+ if (test) return true;
+ StatusView d = new StatusView(path, vc);
+ MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public StatusView(string filepath, VersionControlSystem vc)
+ : base(Path.GetFileName(filepath) + " Status") {
+ this.vc = vc;
+ this.filepath = filepath;
+
+ main = new VBox(false, 5);
+ widget = main;
+ main.Show();
+
+ commandbar = new HBox(false, 5);
+ main.PackStart(commandbar, false, false, 5);
+
+ showRemoteStatus = new Button("Show Remote Status");
+ commandbar.PackEnd(showRemoteStatus, false, false, 0);
+ showRemoteStatus.Clicked += new EventHandler(OnShowRemoteStatusClicked);
+
+ buttonCommit = new Button("Commit...");
+ commandbar.PackEnd(buttonCommit, false, false, 0);
+ buttonCommit.Clicked += new EventHandler(OnCommitClicked);
+
+ boxCommit = new VBox(false, 2);
+ textCommitMessage = new TextView();
+ HBox boxCommitButtons = new HBox(false, 2);
+ buttonCommitCancel = new Button("Cancel");
+ buttonCommitCommit = new Button("Commit");
+ textCommitMessage.Show();
+ buttonCommitCancel.Show();
+ buttonCommitCommit.Show();
+ boxCommit.PackStart(textCommitMessage, true, true, 0);
+ boxCommit.PackStart(boxCommitButtons, false, false, 0);
+ boxCommitButtons.PackEnd(buttonCommitCancel, false, false, 0);
+ boxCommitButtons.PackEnd(buttonCommitCommit, false, false, 0);
+ buttonCommitCancel.Clicked += new EventHandler(OnCommitCancelClicked);
+ buttonCommitCommit.Clicked += new EventHandler(OnCommitCommitClicked);
+
+ ScrolledWindow scroller = new ScrolledWindow();
+ Viewport viewport = new Viewport();
+ box = new VBox(false, 5);
+ main.Add(scroller);
+
+ viewport.Add(box);
+ scroller.Add(viewport);
+
+ box.Show();
+
+ StartUpdate();
+ }
+
+ public override Gtk.Widget Control {
+ get {
+ return widget;
+ }
+ }
+
+ private void StartUpdate() {
+ if (table != null) {
+ box.Remove(table);
+ table.Destroy();
+ table = null;
+ }
+
+ if (status == null) {
+ status = new Label();
+ box.Add(status);
+ status.Show();
+ }
+ if (!remoteStatus)
+ status.Text = "Scanning for changes...";
+ else
+ status.Text = "Scanning for local and remote changes...";
+
+ showRemoteStatus.Sensitive = false;
+ buttonCommit.Sensitive = false;
+
+ new Worker(vc, filepath, remoteStatus, this).Start();
+ }
+
+ private class HeaderLabel : Label {
+ public HeaderLabel(string text) : base() {
+ Markup = "<b>" + text + "</b>";
+ Show();
+ }
+ }
+
+ private void Update() {
+ showRemoteStatus.Sensitive = !remoteStatus;
+
+ if (statuses.Length == 0) {
+ if (!remoteStatus)
+ this.status.Text = "No files have local modifications.";
+ else
+ this.status.Text = "No files have local or remote modifications.";
+ return;
+ }
+
+ buttonsShowLog = new Hashtable();
+ buttonsShowDiff = new Hashtable();
+
+ box.Remove(this.status);
+ this.status = null;
+
+ if (vc.CanCommit(filepath))
+ buttonCommit.Sensitive = true;
+ checkCommit = new Hashtable();
+
+ table = new Table((uint)statuses.Length+1, (uint)5 + (uint)(remoteStatus ? 2 : 0), false);
+ box.Add(table);
+
+ uint row = 0;
+
+ table.Attach(new HeaderLabel("Status"), 0, 3, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+ table.Attach(new HeaderLabel("Path"), 3, 4, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+
+ if (remoteStatus)
+ table.Attach(new HeaderLabel("Remote Status"), 4, 6, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+
+ for (int i = 0; i < statuses.Length; i++) {
+ Node n = statuses[i];
+
+ RevItem item = new RevItem();
+ item.Path = Path.Combine(filepath, n.LocalRelativePath);
+ item.BaseRev = n.BaseRevision;
+
+ uint col = 0;
+ row++;
+
+ CheckButton check = new CheckButton();
+ checkCommit[check] = item;
+ table.Attach(check, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+ check.Visible = false;
+
+ Gdk.Pixbuf statusicon = VersionControlService.LoadIconForStatus(n.Status);
+ if (n.Status == NodeStatus.Modified) {
+ Button b = new Button();
+ if (statusicon != null) {
+ Image img = new Image(statusicon);
+ img.Show();
+ b.Add(img);
+ } else {
+ b.Label = "Diff";
+ }
+
+ b.Relief = ReliefStyle.Half;
+ buttonsShowDiff[b] = item;
+ table.Attach(b, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+ b.Clicked += new EventHandler(OnShowDiffClicked);
+ b.Show();
+ } else if (statusicon != null) {
+ Image img = new Image(statusicon);
+ img.Show();
+ table.Attach(img, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Fill, 2, 2);
+ } else {
+ ++col;
+ }
+
+ Label status = new Label(n.Status.ToString());
+ status.Show();
+ table.Attach(status, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+
+ Label name = new Label();
+ name.Justify = Justification.Left;
+ name.Layout.Alignment = Pango.Alignment.Left;
+ name.Xalign = 0;
+ name.Text = n.LocalRelativePath;
+ name.Show();
+ table.Attach(name, col, ++col, row, row+1, AttachOptions.Expand, AttachOptions.Shrink, 2, 2);
+
+ if (remoteStatus) {
+ Label rstatus = new Label(n.RemoteStatus.ToString());
+ rstatus.Show();
+
+ table.Attach(rstatus, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+
+ if (n.RemoteStatus == NodeStatus.Modified) {
+ Button b = new Button("View");
+ b.Relief = ReliefStyle.Half;
+ buttonsShowLog[b] = item;
+ table.Attach(b, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+ b.Clicked += new EventHandler(OnShowLogClicked);
+ b.Show();
+ }
+ }
+ }
+
+ table.Show();
+ }
+
+ private void OnShowRemoteStatusClicked(object src, EventArgs args) {
+ if (commitShown)
+ buttonCommitCancel.Click();
+
+ remoteStatus = true;
+ StartUpdate();
+ }
+
+ private void OnShowLogClicked(object src, EventArgs args) {
+ RevItem file = (RevItem)buttonsShowLog[src];
+ LogView.Show(file.Path, false, file.BaseRev, false);
+ }
+
+ private void OnShowDiffClicked(object src, EventArgs args) {
+ RevItem file = (RevItem)buttonsShowDiff[src];
+ DiffView.Show(file.Path, false);
+ }
+
+ private void OnCommitClicked(object src, EventArgs args) {
+ buttonCommit.Sensitive = false;
+ main.Add(boxCommit);
+ boxCommit.ShowAll();
+ commitShown = true;
+ foreach (CheckButton check in checkCommit.Keys)
+ check.Visible = true;
+ }
+
+ private void OnCommitCommitClicked(object src, EventArgs args) {
+ ArrayList paths = new ArrayList();
+ foreach (CheckButton check in checkCommit.Keys) {
+ if (check.Active) {
+ RevItem file = (RevItem)checkCommit[check];
+ paths.Add(file.Path);
+ }
+ }
+
+ if (paths.Count == 0)
+ return; // TODO: Show message.
+
+ new CommitWorker(
+ vc,
+ (string[])paths.ToArray(typeof(string)),
+ textCommitMessage.Buffer.Text,
+ this).Start();
+
+ OnCommitCancelClicked(null, null);
+ }
+
+ private void OnCommitCancelClicked(object src, EventArgs args) {
+ foreach (CheckButton check in checkCommit.Keys)
+ check.Visible = false;
+ commitShown = false;
+ buttonCommit.Sensitive = true;
+ main.Remove(boxCommit);
+ }
+
+ private class Worker : Task {
+ StatusView view;
+ VersionControlSystem vc;
+ string filepath;
+ bool remoteStatus;
+
+ public Worker(VersionControlSystem vc, string filepath, bool remoteStatus, StatusView view) {
+ this.vc = vc;
+ this.filepath = filepath;
+ this.view = view;
+ this.remoteStatus = remoteStatus;
+ }
+
+ protected override string GetDescription() {
+ return "Retrieving status for " + Path.GetFileName(filepath) + "...";
+ }
+
+ protected override void Run() {
+ view.statuses = vc.GetDirectoryStatus(filepath, remoteStatus, true);
+ }
+
+ protected override void Finished() {
+ view.Update();
+ }
+ }
+
+ private class CommitWorker : Task {
+ StatusView view;
+ VersionControlSystem vc;
+ string[] paths;
+ string message;
+
+ public CommitWorker(VersionControlSystem vc, string[] paths, string message, StatusView view) {
+ this.vc = vc;
+ this.paths = paths;
+ this.message = message;
+ this.view = view;
+ }
+
+ protected override string GetDescription() {
+ return "Committing changes...";
+ }
+
+ protected override void Run() {
+ vc.Commit(paths, message, new UpdateCallback(Callback));
+ }
+
+ protected override void Finished() {
+ view.StartUpdate();
+ }
+
+ void Callback(string path, string action) {
+ Log(action + "\t" + path);
+ }
+ }
+ }
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,66 @@
+using System;
+using System.Collections;
+using System.Threading;
+
+using Gtk;
+
+using MonoDevelop.Core.Services;
+using MonoDevelop.Services;
+
+namespace VersionControlPlugin {
+
+ public abstract class Task {
+ IProgressMonitor tracker;
+ ThreadNotify threadnotify;
+
+ protected abstract string GetDescription();
+
+ // This occurs in the background.
+ protected abstract void Run();
+
+ // This occurs on the main thread when the background
+ // task is complete.
+ protected abstract void Finished();
+
+ protected Task() {
+ threadnotify = new ThreadNotify(new ReadyEvent(Wakeup));
+
+ /*tracker = ((TaskService)ServiceManager.GetService(typeof(TaskService)))
+ .GetStatusProgressMonitor("Version Control", null, true);*/
+ tracker = ((TaskService)ServiceManager.GetService(typeof(TaskService)))
+ .GetOutputProgressMonitor("Version Control", null, true, true);
+ }
+
+ public void Start() {
+ tracker.BeginTask(GetDescription(), 0);
+ new Thread(new ThreadStart(BackgroundWorker)).Start();
+ }
+
+ void BackgroundWorker() {
+ try {
+ Run();
+ tracker.ReportSuccess("Done.");
+ } catch (Exception e) {
+ tracker.ReportError(e.Message, null);
+ Console.Error.WriteLine(e);
+ } finally {
+ threadnotify.WakeupMain();
+ }
+ }
+
+ public void Wakeup() {
+ tracker.EndTask();
+ tracker.Dispose();
+ Finished();
+ }
+
+ protected void Log(string logtext) {
+ tracker.Log.WriteLine(logtext);
+ }
+
+ protected void Warn(string logtext) {
+ tracker.ReportWarning(logtext);
+ }
+
+ }
+}
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,58 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Internal.Parser;
+using MonoDevelop.Services;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Commands;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Widgets;
+
+using VersionControl;
+
+namespace VersionControlPlugin {
+ public class UpdateCommand {
+ public static bool Update(string path, bool test) {
+ foreach (VersionControlSystem vc in VersionControlService.Providers) {
+ if (vc.CanUpdate(path)) {
+ if (test) return true;
+ new UpdateWorker(vc, path).Start();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class UpdateWorker : Task {
+ VersionControlSystem vc;
+ string path;
+
+ public UpdateWorker(VersionControlSystem vc, string path) {
+ this.vc = vc;
+ this.path = path;
+ }
+
+ protected override string GetDescription() {
+ return "Updating " + path + "...";
+ }
+
+ protected override void Run() {
+ vc.Update(path, true, new UpdateCallback(Callback));
+ }
+
+ protected override void Finished() {
+ }
+
+ private void Callback(string path, string action) {
+ Log(action + "\t" + path);
+ }
+ }
+
+ }
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,50 @@
+<AddIn name = "MonoDevelop Version Control Addin"
+ author = "Joshua Tauberer"
+ copyright = "GPL"
+ url = "http://taubz.for.net/code/diff"
+ description = "A MonoDevelop addin for using version control systems like Subversion"
+ version = "0.1">
+
+ <Runtime>
+ <Import assembly="Diff.dll"/>
+ <Import assembly="DiffWidget.dll"/>
+ <Import assembly="VersionControl.dll"/>
+ <Import assembly="VersionControlAddIn.dll"/>
+ </Runtime>
+
+ <Extension path = "/SharpDevelop/Workbench/Pads/MonoDevelop.Gui.Pads.ProjectPad">
+ <PadOption id = "ShowVersionControlOverlays" _label = "Show version control overlay icons" defaultValue = "True" />
+ <NodeBuilder id = "VersionControlNodeExtension" class = "VersionControlPlugin.VersionControlNodeExtension"/>
+ </Extension>
+
+ <Extension path = "/SharpDevelop/Commands">
+ <Command id = "VersionControlPlugin.Commands.Diff"
+ _label = "Diff"
+ description = "Show the changes made to the local copy since the last update."/>
+ <Command id = "VersionControlPlugin.Commands.Log"
+ _label = "Log"
+ description = "Shows the commit history of the file or folder."/>
+ <Command id = "VersionControlPlugin.Commands.Status"
+ _label = "Status"
+ description = "Shows the status of files in the folder."/>
+ <Command id = "VersionControlPlugin.Commands.Update"
+ _label = "Update"
+ description = "Updates the local copy with remote changes."/>
+ </Extension>
+
+ <Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/ProjectFileNode">
+ <CommandItem id = "VersionControlPlugin.Commands.Diff"/>
+ <CommandItem id = "VersionControlPlugin.Commands.Log"/>
+ </Extension>
+ <Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/DefaultDirectoryNode">
+ <CommandItem id = "VersionControlPlugin.Commands.Update"/>
+ <CommandItem id = "VersionControlPlugin.Commands.Status"/>
+ <CommandItem id = "VersionControlPlugin.Commands.Log"/>
+ </Extension>
+ <Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/ProjectBrowserNode">
+ <CommandItem id = "VersionControlPlugin.Commands.Update"/>
+ <CommandItem id = "VersionControlPlugin.Commands.Status"/>
+ <CommandItem id = "VersionControlPlugin.Commands.Log"/>
+ </Extension>
+</AddIn>
+
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_added.png
===================================================================
(Binary files differ)
Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_added.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_conflicted.png
===================================================================
(Binary files differ)
Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_conflicted.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_locked.png
===================================================================
(Binary files differ)
Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_locked.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_modified.png
===================================================================
(Binary files differ)
Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_modified.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_normal.png
===================================================================
(Binary files differ)
Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_normal.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/MonoDevelop/Extras/VersionControl/ChangeLog
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/ChangeLog 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/ChangeLog 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,4 @@
+2005-06-22 Joshua Tauberer <tauberer at for.net>
+
+ * Added VersionControl into repo (but sadly the addin
+ cannot add itself into the repo yet!).
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,727 @@
+/*
+ * Diff Algorithm in C#
+ * Based on Tye McQueen's Algorithm::Diff Perl module version 1.19_01
+ * Converted to C# by Joshua Tauberer <tauberer at for.net>
+ *
+ * The Perl module's copyright notice:
+ * Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved.
+ * Parts by Tye McQueen.
+ *
+ * The Perl module's readme has a ridiculously long list of
+ * thanks for all of the previous authors, who are:
+ * Mario Wolczko (author of SmallTalk code the module is based on)
+ * Ned Konz
+ * Mark-Jason Dominus
+ * Mike Schilli
+ * Amir Karger
+ * Christian Murphy
+ *
+ * The Perl module was released under the Perl Artistic License,
+ * and I leave my additions in the public domain, so I leave
+ * it up to you to figure out what you need to do if you want
+ * to distribute this file in some form.
+ *
+ * Embedded at the end is an IntList class which is based on Mono's
+ * ArrayList class. When everybody has C# generics, it will disappear.
+ */
+
+
+using System;
+using System.Collections;
+using System.Text;
+
+//using IntList = System.Collections.Generic.List<int>;
+//using TrioList = System.Collections.Generic.List<Algorithm.Diff.Trio>;
+//using IntList = System.Collections.ArrayList;
+using TrioList = System.Collections.ArrayList;
+
+namespace Algorithm.Diff {
+ public interface IDiff : IEnumerable {
+ IList Left { get; }
+ IList Right { get; }
+ }
+
+ public abstract class Hunk {
+ internal Hunk() { }
+
+ public abstract int ChangedLists { get; }
+
+ public abstract bool Same { get; }
+ public abstract bool Conflict { get; }
+
+ public abstract bool IsSame(int index);
+
+ public abstract Range Original();
+ public abstract Range Changes(int index);
+
+ public int MaxLines() {
+ int m = Original().Count;
+ for (int i = 0; i < ChangedLists; i++)
+ if (Changes(i).Count > m)
+ m = Changes(i).Count;
+ return m;
+ }
+ }
+
+ public class Diff : IDiff {
+ internal IList left, right;
+ IComparer comparer;
+ IHashCodeProvider hashcoder;
+
+ public IList Left { get { return left; } }
+ public IList Right { get { return right; } }
+
+ private class Trio {
+ public Trio a;
+ public int b, c;
+ public Trio(Trio a, int b, int c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+ }
+
+ public class Hunk : Algorithm.Diff.Hunk {
+ IList left, right;
+ int s1start, s1end, s2start, s2end;
+ bool same;
+
+ internal Hunk(IList left, IList right, int s1start, int s1end, int s2start, int s2end, bool same) {
+ this.left = left;
+ this.right = right;
+ this.s1start = s1start;
+ this.s1end = s1end;
+ this.s2start = s2start;
+ this.s2end = s2end;
+ this.same = same;
+ }
+
+ internal void SetLists(IList left, IList right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public override int ChangedLists { get { return 1; } }
+
+ public override bool Same { get { return same; } }
+
+ public override bool Conflict { get { return false; } }
+
+ public override bool IsSame(int index) {
+ if (index != 0) throw new ArgumentException();
+ return Same;
+ }
+
+ private Range get(int seq) {
+ int start = (seq==1?s1start:s2start);
+ int end = (seq==1?s1end:s2end);
+ IList list = (seq==1?left:right);
+ if (end < start) return new Range(list, start, 0);
+ return new Range(list, start, end-start+1);
+ }
+
+ public Range Left { get { return get(1); } }
+ public Range Right { get { return get(2); } }
+
+ public override Range Original() { return Left; }
+ public override Range Changes(int index) {
+ if (index != 0) throw new ArgumentException();
+ return Right;
+ }
+
+ public override int GetHashCode() {
+ return unchecked(s1start + s1end + s2start + s2end);
+ }
+
+ public override bool Equals(object o) {
+ Hunk h = o as Hunk;
+ return
+ s1start == h.s1start &&
+ s1start == h.s1end &&
+ s1start == h.s2start &&
+ s1start == h.s2end &&
+ same == h.same;
+ }
+
+ public override string ToString() {
+ if (left == null || right == null)
+ return base.ToString();
+ return DiffString();
+ }
+
+ public string DiffString() {
+ if (left == null || right == null)
+ throw new InvalidOperationException("This hunk is based on a patch which does not have the compared data.");
+
+ StringBuilder ret = new StringBuilder();
+
+ if (Same) {
+ foreach (object item in Left) {
+ ret.Append(" ");
+ ret.Append(item.ToString());
+ ret.Append("\n");
+ }
+ } else {
+ foreach (object item in Left) {
+ ret.Append("<");
+ ret.Append(item.ToString());
+ ret.Append("\n");
+ }
+ foreach (object item in Right) {
+ ret.Append(">");
+ ret.Append(item.ToString());
+ ret.Append("\n");
+ }
+ }
+
+ return ret.ToString();
+ }
+
+ internal Hunk Crop(int shiftstart, int shiftend) {
+ return new Diff.Hunk(left, right, Left.Start+shiftstart, Left.End-shiftend, Right.Start+shiftstart, Right.End-shiftend, same);
+ }
+
+ internal Hunk Reverse() {
+ return new Diff.Hunk(right, left, Right.Start, Right.End, Left.Start, Left.End, same);
+ }
+ }
+
+ public Diff(IList left, IList right, IComparer comparer, IHashCodeProvider hashcoder) {
+ this.left = left;
+ this.right = right;
+ this.comparer = comparer;
+ this.hashcoder = hashcoder;
+ init();
+ }
+
+ public Diff(string leftFile, string rightFile, bool caseSensitive, bool compareWhitespace)
+ : this(UnifiedDiff.LoadFileLines(leftFile), UnifiedDiff.LoadFileLines(rightFile), caseSensitive, compareWhitespace) {
+ }
+
+ public Diff(string[] left, string[] right, bool caseSensitive, bool compareWhitespace)
+ : this(
+ StripWhitespace(left, !compareWhitespace),
+ StripWhitespace(right, !compareWhitespace),
+ caseSensitive ? (IComparer)Comparer.Default : (IComparer)CaseInsensitiveComparer.Default,
+ caseSensitive ? null : CaseInsensitiveHashCodeProvider.Default
+ ) {
+ }
+
+ ////////////////////////////////////////////////////////////
+
+
+ private static string[] StripWhitespace(string[] lines, bool strip) {
+ if (!strip) return lines;
+ string[] ret = new string[lines.Length];
+ for (int i = 0; i < lines.Length; i++) {
+ StringBuilder sb = new StringBuilder();
+ foreach (char c in lines[i])
+ if (!char.IsWhiteSpace(c))
+ sb.Append(c);
+ ret[i] = sb.ToString();
+ }
+ return ret;
+ }
+
+ ////////////////////////////////////////////////////////////
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ if (cdif == null)
+ throw new InvalidOperationException("No comparison has been performed.");
+ return new Enumerator(this);
+ }
+
+ public override string ToString() {
+ System.IO.StringWriter w = new System.IO.StringWriter();
+ UnifiedDiff.WriteUnifiedDiff(this, w);
+ return w.ToString();
+ }
+
+ public Patch CreatePatch() {
+ int ctr = 0;
+ foreach (Hunk hunk in this)
+ if (!hunk.Same)
+ ctr += hunk.Right.Count;
+
+ object[] rightData = new object[ctr];
+
+ ArrayList hunks = new ArrayList();
+ ctr = 0;
+ foreach (Hunk hunk in this) {
+ if (hunk.Same) {
+ hunks.Add(new Patch.Hunk(rightData, hunk.Left.Start, hunk.Left.Count, 0, 0, true));
+ } else {
+ hunks.Add(new Patch.Hunk(rightData, hunk.Left.Start, hunk.Left.Count, ctr, hunk.Right.Count, false));
+ for (int i = 0; i < hunk.Right.Count; i++)
+ rightData[ctr++] = hunk.Right[i];
+ }
+ }
+
+
+ return new Patch((Patch.Hunk[])hunks.ToArray(typeof(Hunk)));
+ }
+
+ /*
+ # McIlroy-Hunt diff algorithm
+ # Adapted from the Smalltalk code of Mario I. Wolczko, <mario at wolczko.com>
+ # by Ned Konz, perl at bike-nomad.com
+ # Updates by Tye McQueen, http://perlmonks.org/?node=tye
+
+ # Create a hash that maps each element of $aCollection to the set of
+ # positions it occupies in $aCollection, restricted to the elements
+ # within the range of indexes specified by $start and $end.
+ # The fourth parameter is a subroutine reference that will be called to
+ # generate a string to use as a key.
+ # Additional parameters, if any, will be passed to this subroutine.
+ #
+ # my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
+ */
+
+ Hashtable _withPositionsOfInInterval(IList aCollection, int start, int end) {
+ Hashtable d = new Hashtable(hashcoder, comparer);
+ for (int index = start; index <= end; index++) {
+ object element = aCollection[index];
+ if (d.ContainsKey(element)) {
+ IntList list = (IntList)d[element];
+ list.Add(index);
+ } else {
+ IntList list = new IntList();
+ list.Add(index);
+ d[element] = list;
+ }
+ }
+ foreach (IntList list in d.Values)
+ list.Reverse();
+ return d;
+ }
+
+ /*
+ # Find the place at which aValue would normally be inserted into the
+ # array. If that place is already occupied by aValue, do nothing, and
+ # return undef. If the place does not exist (i.e., it is off the end of
+ # the array), add it to the end, otherwise replace the element at that
+ # point with aValue. It is assumed that the array's values are numeric.
+ # This is where the bulk (75%) of the time is spent in this module, so
+ # try to make it fast!
+ */
+ // NOTE: Instead of returning undef, it returns -1.
+ int _replaceNextLargerWith(IntList array, int value, int high) {
+ if (high <= 0)
+ high = array.Count-1;
+
+ // off the end?
+ if (high == -1 || value > (int)array[array.Count-1]) {
+ array.Add(value);
+ return array.Count-1;
+ }
+
+ // binary search for insertion point...
+ int low = 0;
+ int index, found;
+ while (low <= high) {
+ index = (high + low) / 2;
+
+ found = (int)array[index];
+
+ if (value == found)
+ return -1;
+ else if (value > found)
+ low = index + 1;
+ else
+ high = index - 1;
+ }
+
+ // # now insertion point is in $low.
+ array[low] = value; // overwrite next larger
+ return low;
+ }
+
+ /*
+ # This method computes the longest common subsequence in $a and $b.
+
+ # Result is array or ref, whose contents is such that
+ # $a->[ $i ] == $b->[ $result[ $i ] ]
+ # foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
+
+ # An additional argument may be passed; this is a hash or key generating
+ # function that should return a string that uniquely identifies the given
+ # element. It should be the case that if the key is the same, the elements
+ # will compare the same. If this parameter is undef or missing, the key
+ # will be the element as a string.
+
+ # By default, comparisons will use "eq" and elements will be turned into keys
+ # using the default stringizing operator '""'.
+
+ # Additional parameters, if any, will be passed to the key generation
+ # routine.
+ */
+
+ bool compare(object a, object b) {
+ if (comparer == null) return a.Equals(b);
+ return comparer.Compare(a, b) == 0;
+ }
+
+ bool IsPrepared(out Hashtable bMatches) {
+ bMatches = null;
+ return false;
+ }
+
+ IntList _longestCommonSubsequence(IList a, IList b) {
+ int aStart = 0;
+ int aFinish = a.Count-1;
+ IntList matchVector = new IntList();
+ Hashtable bMatches;
+
+ // initialize matchVector to length of a
+ for (int i = 0; i < a.Count; i++)
+ matchVector.Add(-1);
+
+ if (!IsPrepared(out bMatches)) {
+ int bStart = 0;
+ int bFinish = b.Count-1;
+
+ // First we prune off any common elements at the beginning
+ while (aStart <= aFinish && bStart <= bFinish && compare(a[aStart], b[bStart]))
+ matchVector[aStart++] = bStart++;
+
+ // now the end
+ while (aStart <= aFinish && bStart <= bFinish && compare(a[aFinish], b[bFinish]))
+ matchVector[aFinish--] = bFinish--;
+
+ // Now compute the equivalence classes of positions of elements
+ bMatches =
+ _withPositionsOfInInterval(b, bStart, bFinish);
+ }
+
+ IntList thresh = new IntList();
+ TrioList links = new TrioList();
+
+ for (int i = aStart; i <= aFinish; i++) {
+ IntList aimatches = (IntList)bMatches[a[i]];
+ if (aimatches != null) {
+ int k = 0;
+ for (int ji = 0; ji < aimatches.Count; ji++) {
+ int j = aimatches[ji];
+ // # optimization: most of the time this will be true
+ if (k>0 && (int)thresh[k] > j && (int)thresh[k-1] < j)
+ thresh[k] = j;
+ else
+ k = _replaceNextLargerWith(thresh, j, k);
+
+ // oddly, it's faster to always test this (CPU cache?).
+ if (k != -1) {
+ Trio t = new Trio( (Trio)( k>0 ? links[k-1] : null ), i, j );
+ if (k == links.Count)
+ links.Add( t );
+ else
+ links[k] = t;
+ }
+ }
+ }
+ }
+
+ if (thresh.Count > 0) {
+ for (Trio link = (Trio)links[thresh.Count-1]; link != null; link = link.a)
+ matchVector[link.b] = link.c;
+ }
+
+ return matchVector;
+ }
+
+ /*void prepare(IList list) {
+ prepared = _withPositionsOfInInterval(list, 0, list.Count-1);
+ preparedlist = list;
+ }*/
+
+ void LCSidx(IList a, IList b, out IntList am, out IntList bm) {
+ IntList match = _longestCommonSubsequence(a, b);
+ am = new IntList();
+ for (int i = 0; i < match.Count; i++)
+ if ((int)match[i] != -1)
+ am.Add(i);
+ bm = new IntList();
+ for (int vi = 0; vi < am.Count; vi++)
+ bm.Add(match[am[vi]]);
+ }
+
+ IntList compact_diff(IList a, IList b) {
+ IntList am, bm, cdiff;
+ LCSidx(a, b, out am, out bm);
+ cdiff = new IntList();
+ int ai = 0, bi = 0;
+ cdiff.Add(ai);
+ cdiff.Add(bi);
+ while (true) {
+ while(am.Count > 0 && ai == (int)am[0] && bi == (int)bm[0]) {
+ am.RemoveAt(0);
+ bm.RemoveAt(0);
+ ++ai;
+ ++bi;
+ }
+
+ cdiff.Add(ai);
+ cdiff.Add(bi);
+ if (am.Count == 0) break;
+ ai = (int)am[0];
+ bi = (int)bm[0];
+ cdiff.Add(ai);
+ cdiff.Add(bi);
+ }
+
+ if (ai < a.Count || bi < b.Count) {
+ cdiff.Add(a.Count);
+ cdiff.Add(b.Count);
+ }
+
+ return cdiff;
+ }
+
+ int _End;
+ bool _Same;
+ IntList cdif = null;
+
+ void init() {
+ cdif = compact_diff(left, right);
+ _Same = true;
+ if (0 == (int)cdif[2] && 0 == (int)cdif[3]) {
+ _Same = false;
+ cdif.RemoveAt(0);
+ cdif.RemoveAt(0);
+ }
+
+ _End = (1+cdif.Count)/2;
+ }
+
+ private class Enumerator : IEnumerator {
+ Diff diff;
+ int _Pos, _Off;
+
+ public Enumerator(Diff diff) {
+ this.diff = diff;
+ Reset();
+ }
+
+ public object Current { get { _ChkPos(); return gethunk(); } }
+
+ public bool MoveNext() { return next(); }
+
+ public void Reset() { reset(0); }
+
+ void _ChkPos() {
+ if (_Pos == 0) throw new InvalidOperationException("Position is reset.");
+ }
+
+ void reset(int pos) {
+ if (pos < 0 || diff._End <= pos) pos = -1;
+ _Pos = pos;
+ _Off = 2*pos - 1;
+ }
+
+ bool next() {
+ reset(_Pos+1);
+ return _Pos != -1;
+ }
+
+ Hunk gethunk() {
+ _ChkPos();
+
+ int a1, a2, b1, b2;
+
+ int off1 = 1 + _Off;
+ int off2 = 2 + _Off;
+
+ a1 = (int)diff.cdif[off1-2];
+ a2 = (int)diff.cdif[off1] - 1;
+ b1 = (int)diff.cdif[off2-2];
+ b2 = (int)diff.cdif[off2] - 1;
+
+ bool s = same();
+ return new Hunk(diff.left, diff.right, a1, a2, b1, b2, s);
+ }
+
+ bool same() {
+ _ChkPos();
+ if (diff._Same != ((1 & _Pos) != 0))
+ return false;
+ return true;
+ }
+ }
+ }
+
+ internal class IntList {
+ private const int DefaultInitialCapacity = 0x10;
+ private int _size;
+ private int[] _items;
+
+ public IntList() {
+ _items = new int[DefaultInitialCapacity];
+ }
+
+ public int this[int index] {
+ get { return _items[index]; }
+ set { _items[index] = value; }
+ }
+
+ public int Count { get { return _size; } }
+
+ private void EnsureCapacity(int count) {
+ if (count <= _items.Length) return;
+ int newLength;
+ int[] newData;
+ newLength = _items.Length << 1;
+ if (newLength == 0)
+ newLength = DefaultInitialCapacity;
+ while (newLength < count)
+ newLength <<= 1;
+ newData = new int[newLength];
+ Array.Copy(_items, 0, newData, 0, _items.Length);
+ _items = newData;
+ }
+
+ private void Shift(int index, int count) {
+ if (count > 0) {
+ if (_size + count > _items.Length) {
+ int newLength;
+ int[] newData;
+ newLength = (_items.Length > 0) ? _items.Length << 1 : 1;
+ while (newLength < _size + count)
+ newLength <<= 1;
+ newData = new int[newLength];
+ Array.Copy(_items, 0, newData, 0, index);
+ Array.Copy(_items, index, newData, index + count, _size - index);
+ _items = newData;
+ } else {
+ Array.Copy(_items, index, _items, index + count, _size - index);
+ }
+ } else if (count < 0) {
+ int x = index - count ;
+ Array.Copy(_items, x, _items, index, _size - x);
+ }
+ }
+
+ public int Add(int value) {
+ if (_items.Length <= _size /* same as _items.Length < _size + 1) */)
+ EnsureCapacity(_size + 1);
+ _items[_size] = value;
+ return _size++;
+ }
+
+ public virtual void Clear() {
+ Array.Clear(_items, 0, _size);
+ _size = 0;
+ }
+
+ public virtual void RemoveAt(int index) {
+ if (index < 0 || index >= _size)
+ throw new ArgumentOutOfRangeException("index", index,
+ "Less than 0 or more than list count.");
+ Shift(index, -1);
+ _size--;
+ }
+
+ public void Reverse() {
+ for (int i = 0; i <= Count / 2; i++) {
+ int t = this[i];
+ this[i] = this[Count-i-1];
+ this[Count-i-1] = t;
+ }
+ }
+ }
+
+ public class Range : IList {
+ IList list;
+ int start, count;
+
+ static ArrayList EmptyList = new ArrayList();
+
+ public Range(IList list, int start, int count) {
+ this.list = list;
+ this.start = start;
+ this.count = count;
+ }
+
+ public int Start { get { return start; } }
+
+ public int Count { get { return count; } }
+
+ public int End { get { return start+count-1; } }
+
+ private void Check() {
+ if (count > 0 && list == null)
+ throw new InvalidOperationException("This range does not refer to a list with data.");
+ }
+
+ public object this[int index] {
+ get {
+ Check();
+ if (index < 0 || index >= count)
+ throw new ArgumentException("index");
+ return list[index+start];
+ }
+ }
+
+ // IEnumerable Functions
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ if (count == 0 && list == null) return EmptyList.GetEnumerator();
+ Check();
+ return new Enumer(this);
+ }
+
+ private class Enumer : IEnumerator {
+ Range list;
+ int index = -1;
+ public Enumer(Range list) { this.list = list; }
+ public void Reset() { index = -1; }
+ public bool MoveNext() {
+ index++;
+ return index < list.Count;
+ }
+ public object Current { get { return list[index]; } }
+ }
+
+ // ICollection Functions
+
+ void ICollection.CopyTo(Array array, int index) {
+ Check();
+ for (int i = 0; i < Count; i++)
+ array.SetValue(this[i], i + index);
+ }
+ object ICollection.SyncRoot {
+ get { return null; }
+ }
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ // IList Functions
+
+ bool IList.IsFixedSize { get { return true; } }
+
+ bool IList.IsReadOnly { get { return true; } }
+
+ object IList.this[int index] { set { throw new InvalidOperationException(); } }
+
+ int IList.Add(object obj) { throw new InvalidOperationException(); }
+
+ void IList.Clear() { throw new InvalidOperationException(); }
+
+ void IList.Insert(int index, object obj) { throw new InvalidOperationException(); }
+
+ void IList.Remove(object obj) { throw new InvalidOperationException(); }
+
+ void IList.RemoveAt(int index) { throw new InvalidOperationException(); }
+
+ public bool Contains(object obj) {
+ return IndexOf(obj) != -1;
+ }
+
+ public int IndexOf(object obj) {
+ for (int i = 0; i < Count; i++)
+ if (obj.Equals(this[i]))
+ return i;
+ return -1;
+ }
+ }
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,18 @@
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+ASSEMBLY = $(ADDIN_BUILD)/Diff.dll
+
+FILES = *.cs
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+
+$(ASSEMBLY): $(FILES)
+ mkdir -p $(ADDIN_BUILD)
+ $(CSC) $(CSC_FLAGS) $(FILES) -out:$@ -target:library
+
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES)
+
+all: $(ASSEMBLY)
+
+include $(top_srcdir)/Makefile.include
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,299 @@
+/*
+ * Merges, a supporting class for Diffs
+ */
+
+using System;
+using System.Collections;
+using System.Text;
+
+namespace Algorithm.Diff {
+ public class Merge : IEnumerable {
+ IDiff[] diffs;
+ ArrayList hunks = new ArrayList();
+
+ public Merge(IList original, IList[] changed, IComparer comparer, IHashCodeProvider hashcoder)
+ : this(makediffs(original, changed, comparer, hashcoder))
+ {
+ }
+
+ public Merge(string original, string[] changed, IComparer comparer)
+ : this(makediffs(original, changed, comparer))
+ {
+ }
+
+ private static IDiff[] makediffs(IList original, IList[] changed, IComparer comparer, IHashCodeProvider hashcoder) {
+ IDiff[] diffs = new IDiff[changed.Length];
+ for (int i = 0; i < changed.Length; i++)
+ diffs[i] = new Diff(original, changed[i], comparer, hashcoder);
+ return diffs;
+ }
+
+ private static IDiff[] makediffs(string original, string[] changed, IComparer comparer) {
+ IDiff[] diffs = new IDiff[changed.Length];
+ for (int i = 0; i < changed.Length; i++)
+ diffs[i] = new TextDiff(original, changed[i], comparer);
+ return diffs;
+ }
+
+ public Merge(IDiff[] diffs) {
+ this.diffs = diffs;
+
+ // initialize data structures
+
+ IEnumerator[] enumerators = new IEnumerator[diffs.Length];
+ ArrayList[] hunks = new ArrayList[diffs.Length];
+
+ for (int i = 0; i < hunks.Length; i++) {
+ enumerators[i] = ((IEnumerable)diffs[i]).GetEnumerator();
+ hunks[i] = new ArrayList();
+ }
+
+ int startline = 0;
+
+ while (true) {
+ int endline = -1;
+ bool hasmore = false;
+
+ // Get the next hunk for each diff, and find the longest
+ // hunk for which there are changes.
+
+ for (int i = 0; i < hunks.Length; i++) {
+ if (hunks[i].Count > 0) continue;
+ if (!enumerators[i].MoveNext()) return;
+ hasmore = true;
+ Diff.Hunk hunk = (Diff.Hunk)enumerators[i].Current;
+ hunks[i].Add(hunk);
+ if (!hunk.Same && hunk.Left.End > endline)
+ endline = hunk.Left.End;
+ }
+
+ if (!hasmore) return;
+
+ if (endline == -1) {
+ // All of the hunks represented no change. Find the shortest hunk,
+ // create a hunk from the current start line to the end of the
+ // shortest hunk, and retain all of the hunks that overlap into that
+ // hunk's next region. (Clear the rest.)
+ int start = int.MaxValue;
+ for (int i = 0; i < hunks.Length; i++) {
+ Diff.Hunk h = (Diff.Hunk)hunks[i][0];
+ if (h.Left.End < start) start = h.Left.End;
+ }
+
+ // Crop all of the hunks to the shortest region.
+ Diff.Hunk[][] h2 = new Diff.Hunk[hunks.Length][];
+ for (int i = 0; i < hunks.Length; i++) {
+ h2[i] = new Diff.Hunk[1];
+ h2[i][0] = (Diff.Hunk)hunks[i][0];
+ h2[i][0] = h2[i][0].Crop(startline - h2[i][0].Left.Start, h2[i][0].Left.End - start);
+ }
+ this.hunks.Add( new Hunk(this, h2, startline, start - startline + 1, true) );
+
+ for (int i = 0; i < hunks.Length; i++) {
+ Diff.Hunk h = (Diff.Hunk)hunks[i][0];
+ if (h.Left.End == start) hunks[i].Clear();
+ }
+ startline = start+1;
+ continue;
+ }
+
+ // For each diff, add in all of the non-same hunks that fall
+ // at least partially within the largest hunk region. If
+ // a hunk crosses the edge, push the edge further and then
+ // add more hunks again.
+ bool moreToAdd = true;
+ while (moreToAdd) {
+ moreToAdd = false;
+
+ for (int i = 0; i < hunks.Length; i++) {
+ Diff.Hunk last = (Diff.Hunk)hunks[i][hunks[i].Count-1];
+ while (last.Left.End < endline) {
+ if (!enumerators[i].MoveNext()) continue;
+ last = (Diff.Hunk)enumerators[i].Current;
+ hunks[i].Add(last);
+ if (last.Same) continue;
+ if (last.Left.End > endline) {
+ endline = last.Left.End;
+ moreToAdd = true;
+ }
+ }
+ }
+ }
+
+ Diff.Hunk[][] hunks2 = new Diff.Hunk[hunks.Length][];
+ for (int i = 0; i < hunks.Length; i++) {
+ // any same hunks that overlap the start or end need to be replaced
+ ArrayList hunks3 = new ArrayList();
+ foreach (Diff.Hunk h in hunks[i]) {
+ Diff.Hunk h2 = h;
+ int shiftstart = 0, shiftend = 0;
+ if (h2.Same && h2.Left.Start < startline)
+ shiftstart = startline - h2.Left.Start;
+ if (h2.Same && h2.Left.End > endline)
+ shiftend = h2.Left.End - endline;
+ if (shiftstart != 0 || shiftend != 0)
+ h2 = h2.Crop(shiftstart, shiftend);
+ hunks3.Add(h2);
+ }
+ hunks2[i] = (Diff.Hunk[])hunks3.ToArray(typeof(Diff.Hunk));
+ }
+ this.hunks.Add( new Hunk(this, hunks2, startline, endline - startline + 1, false) );
+
+ // In each hunk list, retain only the last hunk if it
+ // overlaps into the next region.
+ startline = endline+1;
+ for (int i = 0; i < hunks.Length; i++) {
+ if (hunks[i].Count == 0) continue;
+ Diff.Hunk h = (Diff.Hunk)hunks[i][hunks[i].Count-1];
+ hunks[i].Clear();
+ if (h.Left.End >= startline)
+ hunks[i].Add(h);
+ }
+
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return hunks.GetEnumerator();
+ }
+
+ public static IList MergeLists(IList original, IList[] changed, IComparer comparer, IHashCodeProvider hashcoder) {
+ Merge m = new Merge(original, changed, comparer, hashcoder);
+ ArrayList ret = new ArrayList();
+ ArrayList newlines = new ArrayList();
+ foreach (Hunk h in m) {
+ newlines.Clear();
+
+ for (int i = 0; i < changed.Length; i++)
+ if (!h.IsSame(i))
+ newlines.Add(h.Changes(i));
+
+ // If there were no differences in this region, take the original.
+ if (newlines.Count == 0)
+ ret.AddRange(h.Original());
+
+ // If one list has changes, take them
+ else if (newlines.Count == 1)
+ ret.AddRange((Range)newlines[0]);
+
+ // Indicate conflict
+ else
+ ret.Add(new Conflict((Range[])newlines.ToArray(typeof(Range))));
+ }
+ return ret;
+ }
+
+ public class Conflict : IEnumerable {
+ Range[] ranges;
+
+ internal Conflict(Range[] ranges) {
+ this.ranges = ranges;
+ }
+
+ public Range[] Ranges { get { return ranges; } }
+
+ IEnumerator IEnumerable.GetEnumerator() { return ranges.GetEnumerator(); }
+
+ public override string ToString() {
+ StringBuilder b = new StringBuilder();
+ b.Append("<<<<<<<<<<\n");
+ for (int i = 0; i < ranges.Length; i++) {
+ if (i > 0) b.Append("----------\n");
+ foreach (object item in ranges[i]) {
+ b.Append(item);
+ b.Append("\n");
+ }
+ }
+ b.Append(">>>>>>>>>>\n");
+ return b.ToString();
+ }
+ }
+
+ public class Hunk : Algorithm.Diff.Hunk {
+ Merge merge;
+ Diff.Hunk[][] hunks;
+ int start, count;
+ bool same, conflict;
+
+ internal Hunk(Merge merge, Diff.Hunk[][] hunks, int start, int count, bool same) {
+ this.merge = merge;
+ this.hunks = hunks;
+ this.start = start;
+ this.count = count;
+ this.same = same;
+
+ int ct = 0;
+ foreach (Diff.Hunk[] hh in hunks) {
+ foreach (Diff.Hunk h in hh) {
+ if (!h.Same) {
+ ct++;
+ break;
+ }
+ }
+ }
+ conflict = (ct > 1);
+ }
+
+ public override int ChangedLists { get { return merge.diffs.Length; } }
+
+ // Returns the set of changes within this hunk's range for the
+ // diff of the given index.
+ public Diff.Hunk[] ChangesHunks(int index) {
+ return hunks[index];
+ }
+
+ public int ChangedIndex() {
+ if (Conflict) throw new InvalidOperationException("ChangedIndex cannot be called if there is a conflict.");
+ for (int i = 0; i < hunks.Length; i++) {
+ foreach (Diff.Hunk h in hunks[i])
+ if (!h.Same) return i;
+ }
+ return -1;
+ }
+
+ // Returns the range of elements corresponding to this hunk's range, in the original.
+ public override Range Original() {
+ return new Range(merge.diffs[0].Left, start, count);
+ }
+
+ // Returns the range of elements corresponding to this hunk's range, in the diff of the given index.
+ public override Range Changes(int index) {
+ return new Range(merge.diffs[index].Right, hunks[index][0].Right.Start, hunks[index][hunks[index].Length-1].Right.End - hunks[index][0].Right.Start + 1);
+ }
+
+ public override bool Same { get { return same; } }
+
+ public override bool Conflict { get { return conflict; } }
+
+ public override bool IsSame(int index) {
+ foreach (Diff.Hunk h in hunks[index])
+ if (!h.Same) return false;
+ return true;
+ }
+
+ public override string ToString() {
+ StringBuilder b = new StringBuilder();
+ if (Same) {
+ foreach (object item in Original()) {
+ b.Append(" ");
+ b.Append(item);
+ b.Append("\n");
+ }
+ return b.ToString();
+ }
+
+ b.Append("==========\n");
+ for (int i = 0; i < hunks.Length; i++) {
+ if (i > 0)
+ b.Append("----------\n");
+ foreach (Diff.Hunk h in hunks[i])
+ b.Append(h);
+ }
+ b.Append("==========\n");
+ return b.ToString();
+ }
+
+ }
+ }
+}
+
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,59 @@
+/*
+ * Patches, a supporting class for Diffs
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+using System.Xml;
+
+namespace Algorithm.Diff {
+
+ public class Patch : IEnumerable {
+ Hunk[] hunks;
+
+ internal Patch(Hunk[] hunks) {
+ this.hunks = hunks;
+ }
+
+ public class Hunk {
+ object[] rightData;
+ int leftstart, leftcount, rightstart, rightcount;
+ bool same;
+
+ internal Hunk(object[] rightData, int st, int c, int rs, int rc, bool s) {
+ this.rightData = rightData;
+ leftstart = st;
+ leftcount = c;
+ rightstart = rs;
+ rightcount = rc;
+ same = s;
+ }
+
+ public bool Same { get { return same; } }
+
+ public int Start { get { return leftstart; } }
+ public int Count { get { return leftcount; } }
+ public int End { get { return leftstart+leftcount-1; } }
+
+ public IList Right { get { if (same) return null; return new Range(rightData, rightstart, rightcount); } }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return hunks.GetEnumerator();
+ }
+
+ public IList Apply(IList original) {
+ ArrayList right = new ArrayList();
+ foreach (Hunk hunk in this) {
+ if (hunk.Same)
+ right.AddRange(new Range(original, hunk.Start, hunk.Count));
+ else
+ right.AddRange(hunk.Right);
+ }
+ return right;
+ }
+
+ }
+}
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/README 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/README 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,5 @@
+This code comes from the Diff library at
+ http://taubz.for.net/code/diff
+which is the Perl Algorithm::Diff module
+converted to C#, and so may be covered by
+the Perl Artistic License.
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,305 @@
+using System;
+using System.Collections;
+using System.Xml;
+
+namespace Algorithm.Diff {
+ public abstract class StructuredDiff {
+ internal Hashtable nodeInterfaces = new Hashtable();
+ internal Hashtable comparisonCache = new Hashtable();
+
+ public virtual void AddInterface(Type type, NodeInterface nodeInterface) {
+ nodeInterfaces[type] = nodeInterface;
+ }
+
+ internal NodeInterface GetInterface(object obj) {
+ bool store = false;
+ Type type = obj.GetType();
+ while (type != null) {
+ NodeInterface ret = (NodeInterface)nodeInterfaces[type];
+ if (ret != null) {
+ if (store) nodeInterfaces[obj.GetType()] = ret;
+ return ret;
+ }
+ type = type.BaseType;
+ store = true;
+ }
+ throw new ArgumentException("Node type has no interface defined: " + obj.GetType());
+ }
+
+ public float CompareLists(IList left, IList right) {
+ return CompareLists(left, right, 0, false);
+ }
+
+ public void Compare(object left, object right) {
+ comparisonCache.Clear();
+ CompareLists(new object[] { left }, new object[] { right }, 0, true);
+ }
+
+ private float CompareLists(IList left, IList right, float threshold, bool output) {
+ // Given two lists, find the elements in the list that correspond.
+ // Two elements correspond if their 'difference metric' is less than
+ // or equal to threshold. For the hunks of correspondent items,
+ // recursively descend into items not truly equal. For hunks of
+ // irreconsiliable material, raise the threshold to the next useful
+ // level and rescan the items.
+
+ if (left.Count == 0 && right.Count == 0)
+ return 0;
+
+ NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this);
+
+ Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this));
+
+ int nitems = 0, ndiffs = 0;
+
+ foreach (Diff.Hunk hunk in diff) {
+ if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) {
+ // This comprises a block of correspondent items who
+ // differ by no more than the threshold value.
+
+ nitems += hunk.Left.Count;
+
+ bool inSameRegion = false;
+
+ for (int i = 0; i < hunk.Left.Count; i++) {
+ object oleft = hunk.Left[i];
+ object oright = hunk.Right[i];
+
+ NodeInterface ileft = GetInterface(oleft);
+ NodeInterface iright = GetInterface(oright);
+
+ IList cleft = null, cright = null;
+ cleft = ileft.GetChildren(oleft);
+ cright = iright.GetChildren(oright);
+
+ float comp = 0;
+ if (ileft == iright)
+ comp = ileft.Compare(oleft, oright, this);
+
+ // If the nodes are equal, emit one node.
+ if (ileft == iright && comp == 0) {
+ if (output) {
+ if (!inSameRegion) { WritePushSame(); inSameRegion = true; }
+ WriteNodeSame(ileft, oleft, oright);
+ }
+
+ // Recurse into the lists of each node.
+ } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) {
+ if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
+ if (output) WritePushNode(ileft, oleft, oright);
+ float d = CompareLists(cleft, cright, 0, output);
+ d *= hunk.Left.Count;
+ if (d < 1) d = 1;
+ ndiffs += (int)d;
+ if (output) WritePopNode();
+
+ // The nodes are not equal, so emit removed and added nodes.
+ } else {
+ if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
+ if (output) WriteNodeChange(ileft, oleft, iright, oright);
+ ndiffs += hunk.Left.Count;
+ }
+ }
+
+ if (output && inSameRegion) WritePopSame();
+ } else {
+ int ct = hunk.Left.Count + hunk.Right.Count;
+ nitems += ct;
+ ndiffs += ct;
+
+ if (output) {
+ bool noRecurse = comparer.minimumDifference >= 1;
+ if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse))
+ WriteNodesRemoved(hunk.Left);
+ if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse))
+ WriteNodesAdded(hunk.Right);
+ if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse)
+ CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output);
+ }
+ }
+ }
+
+ return (float)ndiffs / (float)nitems;
+ }
+
+ protected abstract void WritePushNode(NodeInterface nodeInterface, object left, object right);
+
+ protected abstract void WritePushSame();
+
+ protected abstract void WriteNodeSame(NodeInterface nodeInterface, object left, object right);
+
+ protected abstract void WritePopSame();
+
+ protected abstract void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right);
+
+ protected abstract void WriteNodesRemoved(IList objects);
+
+ protected abstract void WriteNodesAdded(IList objects);
+
+ protected abstract void WritePopNode();
+ }
+
+ public class XmlOutputStructuredDiff : StructuredDiff {
+ bool deepOutput, allContext;
+ XmlWriter output;
+ Hashtable contextNodes;
+
+ public XmlOutputStructuredDiff(XmlWriter output, string context) {
+ this.output = output;
+ this.deepOutput = (context == null);
+ this.allContext = (context != null && context == "*");
+
+ if (!deepOutput && !allContext) {
+ contextNodes = new Hashtable();
+ foreach (string name in context.Split(','))
+ contextNodes[name.Trim()] = contextNodes;
+ }
+ }
+
+ public override void AddInterface(Type type, NodeInterface nodeInterface) {
+ if (!(nodeInterface is XmlOutputNodeInterface))
+ throw new ArgumentException("Node interfaces for the XmlOutputStructuredDiff must implement XmlOutputNodeInterface.");
+ base.AddInterface(type, nodeInterface);
+ }
+
+ protected override void WritePushSame() {
+ }
+
+ protected override void WritePopSame() {
+ }
+
+ protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
+ bool deep = deepOutput;
+
+ if (left is XmlNode && !deepOutput && !allContext) {
+ if (!contextNodes.ContainsKey(((XmlNode)left).Name)) {
+ return;
+ } else {
+ deep = true;
+ }
+ }
+
+ ((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, left, (XmlWriter)output);
+ output.WriteAttributeString("Status", "Same");
+ if (deep)
+ ((XmlOutputNodeInterface)nodeInterface).WriteNodeChildren(left, (XmlWriter)output);
+ output.WriteEndElement();
+ }
+
+ protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
+ ((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, right, (XmlWriter)output);
+ output.WriteAttributeString("Status", "Changed");
+ ((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, left, (XmlWriter)output);
+ ((XmlOutputNodeInterface)leftInterface).WriteNodeChildren(left, (XmlWriter)output);
+ output.WriteEndElement();
+ ((XmlOutputNodeInterface)rightInterface).WriteBeginNode(right, right, (XmlWriter)output);
+ ((XmlOutputNodeInterface)rightInterface).WriteNodeChildren(right, (XmlWriter)output);
+ output.WriteEndElement();
+ output.WriteEndElement();
+ }
+
+ protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
+ ((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, right, output);
+ }
+
+ protected override void WritePopNode() {
+ output.WriteEndElement();
+ }
+
+ void AddRemove(IList objects, string status) {
+ foreach (object obj in objects) {
+ XmlOutputNodeInterface i = (XmlOutputNodeInterface)GetInterface(obj);
+ ((XmlOutputNodeInterface)i).WriteBeginNode(obj, obj, output);
+ output.WriteAttributeString("Status", status);
+ i.WriteBeginNode(obj, obj, output);
+ i.WriteNodeChildren(obj, output);
+ output.WriteEndElement();
+ output.WriteEndElement();
+ }
+ }
+
+ protected override void WriteNodesRemoved(IList objects) {
+ AddRemove(objects, "Removed");
+ }
+ protected override void WriteNodesAdded(IList objects) {
+ AddRemove(objects, "Added");
+ }
+ }
+
+ public abstract class NodeInterface {
+ public abstract IList GetChildren(object node);
+ public abstract float Compare(object left, object right, StructuredDiff comparer);
+ public virtual int GetHashCode(object node) {
+ return node.GetHashCode();
+ }
+ }
+
+ public interface XmlOutputNodeInterface {
+ void WriteNodeChildren(object node, XmlWriter output);
+ void WriteBeginNode(object left, object right, XmlWriter output);
+ }
+
+ internal class HashCodeProvider : IHashCodeProvider {
+ StructuredDiff differ;
+ public HashCodeProvider(StructuredDiff differ) { this.differ = differ; }
+ public int GetHashCode(object obj) {
+ return differ.GetInterface(obj).GetHashCode(obj);
+ }
+ }
+
+ internal class NodeComparerWrapper : IComparer {
+ float threshold;
+ StructuredDiff differ;
+
+ public float minimumDifference = 1;
+
+ bool useCache = true;
+
+ public NodeComparerWrapper(float threshold, StructuredDiff differ) {
+ this.threshold = threshold;
+ this.differ = differ;
+ }
+
+ private class Pair {
+ object left, right;
+ int code;
+ public Pair(object a, object b, StructuredDiff differ) {
+ left = a; right = b;
+ code = unchecked(differ.GetInterface(left).GetHashCode(left) + differ.GetInterface(right).GetHashCode(right));
+ }
+ public override bool Equals(object o) {
+ return ((Pair)o).left == left && ((Pair)o).right == right;
+ }
+ public override int GetHashCode() {
+ return code;
+ }
+ }
+
+ int IComparer.Compare(object left, object right) {
+ float ret;
+
+ Pair pair = new Pair(left, right, differ);
+
+ if (left.GetType() != right.GetType()) {
+ ret = 1;
+ } else if (useCache && differ.comparisonCache.ContainsKey(pair)) {
+ ret = (float)differ.comparisonCache[pair];
+ } else {
+ NodeInterface comparer = differ.GetInterface(left);
+ ret = comparer.Compare(left, right, differ);
+ }
+
+ if (useCache)
+ differ.comparisonCache[pair] = ret;
+
+ if (ret < minimumDifference && ret > threshold)
+ minimumDifference = ret;
+
+ if (ret <= threshold)
+ return 0;
+ else
+ return 1;
+ }
+ }
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,177 @@
+using System;
+using System.Collections;
+
+namespace Algorithm.Diff {
+
+ public class TextDiff : IDiff {
+ Node left, right;
+ ArrayList hunks = new ArrayList();
+
+ public TextDiff(string left, string right) : this (left, right, null) {
+ }
+
+ public TextDiff(string left, string right, IComparer comparer) {
+ this.left = new Node(left, 0, left.Length, 0);
+ this.right = new Node(right, 0, right.Length, 0);
+
+
+ TextDiffStructuredDiff diff = new TextDiffStructuredDiff(hunks, left.ToCharArray(), right.ToCharArray());
+ diff.AddInterface(typeof(Node), new TextDiffNodeInterface(comparer));
+ diff.Compare(this.left, this.right);
+ }
+
+ public IList Left { get { return left.ToString().ToCharArray(); } }
+ public IList Right { get { return right.ToString().ToCharArray(); } }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return hunks.GetEnumerator();
+ }
+
+ private class TextDiffStructuredDiff : StructuredDiff {
+ ArrayList hunks;
+ IList leftlist, rightlist;
+
+ public TextDiffStructuredDiff(ArrayList hunks, IList leftlist, IList rightlist) {
+ this.hunks = hunks;
+ this.leftlist = leftlist;
+ this.rightlist = rightlist;
+ }
+
+ protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
+ }
+
+ protected override void WritePushSame() {
+ }
+
+ private void AddHunk(int lcount, int rcount, bool same) {
+ if (hunks.Count > 0 && same == ((Diff.Hunk)hunks[hunks.Count-1]).Same) {
+ Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
+ hunks[hunks.Count-1] = new Diff.Hunk(
+ leftlist,
+ rightlist,
+ prev.Left.Start,
+ prev.Left.End + lcount,
+ prev.Right.Start,
+ prev.Right.End + rcount,
+ same);
+ return;
+ }
+
+ int le = -1;
+ int re = -1;
+ if (hunks.Count > 0) {
+ Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
+ le = prev.Left.End;
+ re = prev.Right.End;
+ }
+
+ hunks.Add( new Diff.Hunk(
+ leftlist,
+ rightlist,
+ le + 1,
+ le + lcount,
+ re + 1,
+ re + rcount,
+ same) );
+ }
+
+ protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
+ AddHunk(((Node)left).count, ((Node)right).count, true);
+ }
+
+ protected override void WritePopSame() {
+ }
+
+ protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
+ AddHunk(((Node)left).count, ((Node)right).count, false);
+ }
+
+ protected override void WriteNodesRemoved(IList objects) {
+ int start = ((Node)objects[0]).start;
+ int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
+
+ AddHunk(end - start + 1, 0, false);
+ }
+
+ protected override void WriteNodesAdded(IList objects) {
+ int start = ((Node)objects[0]).start;
+ int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
+
+ AddHunk(0, end - start + 1, false);
+ }
+
+ protected override void WritePopNode() {
+ }
+ }
+
+ private class TextDiffNodeInterface : NodeInterface {
+ IComparer comparer;
+
+ public TextDiffNodeInterface(IComparer comparer) { this.comparer = comparer; }
+
+ public override IList GetChildren(object node) {
+ if (((Node)node).children.Count == 0) return null;
+ return ((Node)node).children;
+ }
+
+ private bool Equal(string a, string b) {
+ if (comparer == null)
+ return a == b;
+ return comparer.Compare(a, b) == 0;
+ }
+
+ public override float Compare(object left, object right, StructuredDiff comparer) {
+ string l = left.ToString(), r = right.ToString();
+ if (Equal(l, r)) return 0;
+ if (l.Length == 1 || r.Length == 1) return 1;
+ float d = comparer.CompareLists(GetChildren(left), GetChildren(right));
+ if (((Node)left).level == 2 && d >= .75) d = 1.1f;
+ return d;
+ }
+
+ public override int GetHashCode(object node) {
+ return node.ToString().GetHashCode();
+ }
+ }
+
+ private class Node {
+ public string source;
+ public int start, count;
+
+ public int level;
+ public ArrayList children = new ArrayList();
+
+ static char[][] delims = new char[][] {
+ new char[] { '\n', '\r' },
+ new char[] { ' ', '\t', '.', ',' }
+ };
+
+ public Node(string source, int start, int count, int level) {
+ this.source = source;
+ this.start = start;
+ this.count = count;
+ this.level = level;
+
+ if (level <= 1) {
+ int pos = start;
+ foreach (string child in ToString().Split(delims[level])) {
+ if (child.Length >= 1)
+ children.Add(new Node(source, pos, child.Length, level+1));
+ if (pos + child.Length < count)
+ children.Add(new Node(source, pos+child.Length, 1, level+1));
+ pos += child.Length + 1;
+ }
+ } else if (level == 2) {
+ for (int i = start; i < start + count; i++)
+ children.Add(new Node(source, i, 1, level+1));
+ }
+
+ }
+
+ public override string ToString() {
+ return source.Substring(start, count);
+ }
+ }
+ }
+
+}
Added: trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,157 @@
+/*
+ * A utility class for writing unified diffs.
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+namespace Algorithm.Diff {
+
+ public class UnifiedDiff {
+ private UnifiedDiff() {
+ }
+
+ public static void WriteUnifiedDiff(string[] leftLines, string leftName, string[] rightLines, string rightName, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) {
+ Diff diff = new Diff(leftLines, rightLines, caseSensitive, compareWhitespace);
+ WriteUnifiedDiff(diff, writer, leftName, rightName, context);
+ }
+
+ public static void WriteUnifiedDiff(string leftFile, string rightFile, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) {
+ WriteUnifiedDiff(LoadFileLines(leftFile), leftFile, LoadFileLines(rightFile), rightFile, writer, context, caseSensitive, compareWhitespace);
+ }
+
+ internal static string[] LoadFileLines(string file) {
+ ArrayList lines = new ArrayList();
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(file)) {
+ string s;
+ while ((s = reader.ReadLine()) != null)
+ lines.Add(s);
+ }
+ return (string[])lines.ToArray(typeof(string));
+ }
+
+ public static void WriteUnifiedDiff(Diff diff, TextWriter writer) {
+ WriteUnifiedDiff(diff, writer, "Left", "Right", 2);
+ }
+
+ public static void WriteUnifiedDiff(Diff diff, TextWriter writer, string fromfile, string tofile, int context) {
+ writer.Write("--- ");
+ writer.WriteLine(fromfile);
+ writer.Write("+++ ");
+ writer.WriteLine(tofile);
+
+ ArrayList hunkset = new ArrayList();
+
+ foreach (Diff.Hunk hunk in diff) {
+ Diff.Hunk lasthunk = null;
+ if (hunkset.Count > 0) lasthunk = (Diff.Hunk)hunkset[hunkset.Count-1];
+
+ if (hunk.Same) {
+ // At the start of a hunk set, keep only context lines of context.
+ if (lasthunk == null) {
+ if (hunk.Left.Count > context)
+ hunkset.Add( hunk.Crop(hunk.Left.Count-context, 0) );
+ else
+ hunkset.Add( hunk );
+ // Can't have two same hunks in a row, so the last one was a difference.
+ } else {
+ // Small enough context that this unified diff range will not stop.
+ if (hunk.Left.Count <= context*2) {
+ hunkset.Add( hunk );
+
+ // Too much of the same. Keep context lines and end this section.
+ // And then keep the last context lines as context for the next section.
+ } else {
+ hunkset.Add( hunk.Crop(0, hunk.Left.Count-context) );
+ WriteUnifiedDiffSection(writer, hunkset);
+ hunkset.Clear();
+
+ if (hunk.Left.Count > context)
+ hunkset.Add( hunk.Crop(hunk.Left.Count-context, 0) );
+ else
+ hunkset.Add( hunk );
+ }
+ }
+
+ } else {
+ hunkset.Add(hunk);
+ }
+ }
+
+ if (hunkset.Count > 0 && !(hunkset.Count == 1 && ((Diff.Hunk)hunkset[0]).Same))
+ WriteUnifiedDiffSection(writer, hunkset);
+ }
+
+ private static void WriteUnifiedDiffSection(TextWriter writer, ArrayList hunks) {
+ Diff.Hunk first = (Diff.Hunk)hunks[0];
+ Diff.Hunk last = (Diff.Hunk)hunks[hunks.Count-1];
+
+ writer.Write("@@ -");
+ writer.Write(first.Left.Start+1);
+ writer.Write(",");
+ writer.Write(last.Left.End-first.Left.Start+1);
+ writer.Write(" +");
+ writer.Write(first.Right.Start+1);
+ writer.Write(",");
+ writer.Write(last.Right.End-first.Right.Start+1);
+ writer.WriteLine(" @@");
+
+ foreach (Diff.Hunk hunk in hunks) {
+ if (hunk.Same) {
+ WriteBlock(writer, ' ', hunk.Left);
+ continue;
+ }
+
+ WriteBlock(writer, '-', hunk.Left);
+ WriteBlock(writer, '+', hunk.Right);
+ }
+ }
+
+ private static void WriteBlock(TextWriter writer, char prefix, Range items) {
+ if (items.Count > 0 && items[0] is char)
+ WriteCharBlock(writer, prefix, items);
+ else
+ WriteStringBlock(writer, prefix, items);
+ }
+
+ private static void WriteStringBlock(TextWriter writer, char prefix, Range items) {
+ foreach (object item in items) {
+ writer.Write(prefix);
+ writer.WriteLine(item.ToString());
+ }
+ }
+
+ private static void WriteCharBlock(TextWriter writer, char prefix, Range items) {
+ bool newline = true;
+ int counter = 0;
+ foreach (char c in items) {
+ if (c == '\n' && !newline) {
+ writer.WriteLine();
+ newline = true;
+ }
+
+ if (newline) {
+ writer.Write(prefix);
+ newline = false;
+ counter = 0;
+ }
+
+ if (c == '\n') {
+ writer.WriteLine("[newline]");
+ newline = true;
+ } else {
+ writer.Write(c);
+ counter++;
+ if (counter == 60) {
+ writer.WriteLine();
+ newline = true;
+ }
+ }
+ }
+ if (!newline) writer.WriteLine();
+ }
+ }
+}
+
Added: trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,19 @@
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+ASSEMBLY = $(ADDIN_BUILD)/DiffWidget.dll
+
+DLLS =
+FILES = *.cs
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+
+$(ASSEMBLY): $(FILES)
+ mkdir -p $(ADDIN_BUILD)
+ $(CSC) $(CSC_FLAGS) $(FILES) -out:$@ -target:library -r:$(ADDIN_BUILD)/Diff.dll $(GTK_SHARP_LIBS)
+
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES)
+
+all: $(ASSEMBLY)
+
+include $(top_srcdir)/Makefile.include
Added: trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,2 @@
+This file creates a Gtk# widget for displaying
+differences between files.
Added: trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,416 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+using Gtk;
+using Pango;
+
+namespace Algorithm.Diff.Gtk {
+
+ public class DiffWidget : VBox {
+
+ ScrolledWindow scroller;
+
+ public double Position {
+ get {
+ return scroller.Vadjustment.Value;
+ }
+ set {
+ scroller.Vadjustment.Value = value;
+ }
+ }
+
+ public class Options {
+ public string LeftName = null;
+ public string RightName = null;
+ public bool SideBySide = false;
+ public bool LineWrap = true;
+ public bool LineNumbers = true;
+ public string Font = "Mono 9";
+ }
+
+ static Gdk.Color
+ ColorChanged = new Gdk.Color (0xFF, 0x99, 0x99),
+ ColorChangedHighlight = new Gdk.Color (0xFF, 0xBB, 0xBB),
+ ColorAdded = new Gdk.Color (0xFF, 0xAA, 0x33),
+ ColorAddedHighlight = new Gdk.Color (0xFF, 0xBB, 0x66),
+ ColorRemoved = new Gdk.Color (0x33, 0xAA, 0xFF),
+ ColorRemovedHighlight = new Gdk.Color (0x66, 0xBB, 0xFF),
+ ColorDefault = new Gdk.Color (0xFF, 0xFF, 0xFF),
+ ColorDefaultHighlight = new Gdk.Color (0xEE, 0xEE, 0xCC),
+ ColorGrey = new Gdk.Color (0x99, 0x99, 0x99),
+ ColorBlack = new Gdk.Color (0, 0, 0);
+
+ public DiffWidget(Diff diff, Options options) : this(Hunkify(diff), options) {
+ }
+
+ public DiffWidget(Merge merge, Options options) : this(Hunkify(merge), options) {
+ }
+
+ private static Hunk[] Hunkify(IEnumerable e) {
+ ArrayList a = new ArrayList();
+ foreach (Hunk x in e)
+ a.Add(x);
+ return (Hunk[])a.ToArray(typeof(Hunk));
+ }
+
+ private DiffWidget(Hunk[] hunks, Options options) : base(false, 0) {
+ if (hunks == null || hunks.Length == 0 || options == null)
+ throw new ArgumentException();
+
+ if (options.SideBySide && options.LeftName != null && options.RightName != null) {
+ HBox filetitles = new HBox(true, 2);
+ PackStart(filetitles, false, false, 2);
+ Label leftlabel = new Label(options.LeftName);
+ Label rightlabel = new Label(options.RightName);
+ filetitles.PackStart(leftlabel);
+ filetitles.PackStart(rightlabel);
+ }
+
+ HBox centerpanel = new HBox(false, 0);
+ PackStart(centerpanel);
+
+ scroller = new ScrolledWindow();
+
+ centerpanel.PackStart(new OverviewRenderer(scroller, hunks, options.SideBySide), false, false, 0);
+
+ Viewport textviewport = new Viewport();
+
+ centerpanel.PackStart(scroller);
+ scroller.Add(textviewport);
+
+ int nRows = 0;
+ foreach (Hunk hunk in hunks) {
+ if (options.SideBySide) {
+ nRows += hunk.MaxLines();
+ } else {
+ if (hunk.Same) {
+ nRows += hunk.Original().Count;
+ } else {
+ for (int i = 0; i < hunk.ChangedLists; i++)
+ nRows += hunk.Changes(i).Count;
+ }
+ }
+ }
+
+ uint nCols = 1 + (uint)hunks[0].ChangedLists;
+ if (options.SideBySide) nCols += 2;
+ if (options.LineNumbers) nCols++;
+
+ VBox tablecontainer = new VBox(false, 0);
+ textviewport.Add(tablecontainer);
+
+ Table difftable = new Table((uint)nRows, (uint)nCols, false);
+ tablecontainer.PackStart(difftable, false, false, 0);
+
+ uint row = 0;
+
+ Pango.FontDescription font = null;
+ if (options.Font != null)
+ font = Pango.FontDescription.FromString(options.Font);
+
+ foreach (Hunk hunk in hunks) {
+ char leftmode = hunk.Same ? ' ' : (hunk.ChangedLists == 1 && hunk.Changes(0).Count == 0) ? '-' : 'C';
+ uint inc = 0;
+
+ if (options.SideBySide) {
+ ComposeLines(hunk.Original(), leftmode, -1, difftable, row, false, 0, options.LineWrap, font, options.LineNumbers);
+ inc = (uint)hunk.Original().Count;
+ } else {
+ if (leftmode == 'C') leftmode = '-';
+ int altlines = -1;
+ if (hunk.ChangedLists == 1 && hunk.Same)
+ altlines = hunk.Changes(0).Start;
+ ComposeLines(hunk.Original(), leftmode, altlines, difftable, row, true, 0, options.LineWrap, font, options.LineNumbers);
+ row += (uint)hunk.Original().Count;
+ }
+
+ for (int i = 0; i < hunk.ChangedLists; i++) {
+ char rightmode = hunk.Same ? ' ' : hunk.Original().Count == 0 ? '+' : 'C';
+
+ if (options.SideBySide) {
+ int colsper = 1 + (options.LineNumbers ? 1 : 0);
+ ComposeLines(hunk.Changes(i), rightmode, -1, difftable, row, false, (uint)((i+1)*colsper), options.LineWrap, font, options.LineNumbers);
+ if (hunk.Changes(i).Count > inc)
+ inc = (uint)hunk.Changes(i).Count;
+ } else {
+ if (rightmode == 'C') rightmode = '+';
+
+ if (!hunk.Same)
+ ComposeLines(hunk.Changes(i), rightmode, -1, difftable, row, true, 0, options.LineWrap, font, options.LineNumbers);
+
+ if (!hunk.Same) row += (uint)hunk.Changes(i).Count;
+ }
+ }
+
+ if (options.SideBySide)
+ row += inc;
+ }
+ }
+
+ void ComposeLines(Algorithm.Diff.Range range, char style, int otherStart, Table table, uint startRow, bool axn, uint col, bool wrap, Pango.FontDescription font, bool lineNumbers) {
+ if (range.Count == 0) return;
+
+ StringBuilder text = new StringBuilder();
+ for (uint i = 0; i < range.Count; i++) {
+ if (axn) {
+ text.Append(style);
+ text.Append(' ');
+ }
+
+ if (lineNumbers) {
+ string lineNo = (range.Start + i + 1).ToString();
+ if (otherStart != -1 && range.Start != otherStart)
+ lineNo += "/" + (otherStart + i + 1).ToString();
+ text.Append(lineNo);
+ text.Append('\t');
+ }
+
+ text.Append((string)range[(int)i]);
+ if (i < range.Count-1) text.Append('\n');
+ }
+ RangeEventBox rangebox = new RangeEventBox(new RangeRenderer(text.ToString(), style, wrap, font));
+ table.Attach(rangebox, 0,1, startRow,(uint)(startRow+range.Count));
+
+ /*
+ int off1 = lineNumbers ? 1 : 0;
+ int off2 = off1 + (axn ? 1 : 0);
+
+ for (uint i = 0; i < range.Count; i++) {
+ if (lineNumbers) {
+ string lineNo = (range.Start + i + 1).ToString();
+ if (otherStart != -1 && range.Start != otherStart)
+ lineNo += "/" + (otherStart + i + 1).ToString();
+
+ Label label = new Label(lineNo);
+ label.ModifyFont( font );
+ label.Yalign = 0;
+ table.Attach(label, col,col+1, startRow+i,startRow+i+1, AttachOptions.Shrink, AttachOptions.Shrink, 1, 1);
+ }
+
+ if (axn) {
+ Label actionlabel = new Label(style.ToString());
+ table.Attach(actionlabel, (uint)(col+off1),(uint)(col+off1+1), startRow+i,startRow+i+1, AttachOptions.Shrink, AttachOptions.Shrink, 1, 1);
+ }
+
+ RangeEventBox line = new RangeEventBox(new RangeRenderer((string)range[(int)i], style, wrap, font));
+ table.Attach(line, (uint)(col+off2),(uint)(col+off2+1), startRow+i,startRow+i+1);
+ }
+ */
+ }
+
+ /*string GetRangeText(Algorithm.Diff.Range range) {
+ string text = "";
+ foreach (string line in range)
+ text += line + "\n";
+ return text;
+ }*/
+
+ class RangeEventBox : EventBox {
+ RangeRenderer renderer;
+
+ public RangeEventBox(RangeRenderer r) {
+ renderer = r;
+ Add(r);
+ this.EnterNotifyEvent += new EnterNotifyEventHandler(EnterNotifyEventHandler);
+ this.LeaveNotifyEvent += new LeaveNotifyEventHandler(LeaveNotifyEventHandler);
+ }
+
+ void EnterNotifyEventHandler (object o, EnterNotifyEventArgs args) {
+ renderer.Highlight();
+ }
+ void LeaveNotifyEventHandler (object o, LeaveNotifyEventArgs args) {
+ renderer.ClearHighlight();
+ }
+ }
+
+ class RangeRenderer : DrawingArea {
+ Pango.Layout layout;
+ Pango.FontDescription font;
+ string text;
+ bool wrap;
+
+ Gdk.Color bg_color;
+ Gdk.Color highlight_bg_color;
+
+ public RangeRenderer(string text, char type, bool wrap, Pango.FontDescription font) {
+ this.text = text;
+ this.wrap = wrap;
+ this.font = font;
+ this.Realized += new EventHandler(OnRealized);
+ this.ExposeEvent += new ExposeEventHandler(OnExposed);
+ this.SizeAllocated += new SizeAllocatedHandler(SizeAllocatedHandler);
+
+ switch (type) {
+ case 'C':
+ bg_color = DiffWidget.ColorChanged;
+ highlight_bg_color = DiffWidget.ColorChangedHighlight;
+ break;
+ case '+':
+ bg_color = DiffWidget.ColorAdded;
+ highlight_bg_color = DiffWidget.ColorAddedHighlight;
+ break;
+ case '-':
+ bg_color = DiffWidget.ColorRemoved;
+ highlight_bg_color = DiffWidget.ColorRemovedHighlight;
+ break;
+ default:
+ bg_color = DiffWidget.ColorDefault;
+ highlight_bg_color = DiffWidget.ColorDefaultHighlight;
+ break;
+ }
+
+ Colormap.AllocColor(ref bg_color, false, true);
+ Colormap.AllocColor(ref highlight_bg_color, false, true);
+ ClearHighlight();
+ }
+
+ public void Highlight() {
+ ModifyBg(StateType.Normal, highlight_bg_color);
+ }
+
+ public void ClearHighlight() {
+ ModifyBg(StateType.Normal, bg_color);
+ }
+
+ void OnExposed (object o, ExposeEventArgs args) {
+ GdkWindow.DrawLayout (this.Style.TextGC (StateType.Normal), 1, 1, layout);
+ }
+
+ void OnRealized (object o, EventArgs args) {
+ layout = new Pango.Layout(PangoContext);
+ layout.SingleParagraphMode = false;
+ layout.FontDescription = font;
+ layout.Indent = (int)(-Pango.Scale.PangoScale * 20);
+
+ layout.SetText(text);
+ Render();
+ }
+
+ void SizeAllocatedHandler (object o, SizeAllocatedArgs args) {
+ Render();
+ }
+
+ void Render() {
+ if (layout == null) return;
+
+ if (wrap)
+ layout.Width = (int)(Allocation.Width * Pango.Scale.PangoScale);
+ else
+ layout.Width = int.MaxValue;
+
+ Rectangle ink, log;
+ layout.GetPixelExtents(out ink, out log);
+ HeightRequest = ink.Y + ink.Height + 3;
+ if (!wrap)
+ WidthRequest = ink.X + ink.Width + 3;
+ }
+ }
+
+ class OverviewRenderer : EventBox {
+ ScrolledWindow scroller;
+ public OverviewRenderer(ScrolledWindow scroller, Hunk[] hunks, bool sidebyside) {
+ this.scroller = scroller;
+ this.ButtonPressEvent += new ButtonPressEventHandler(ButtonPressHandler);
+ Add(new OverviewRenderer2(scroller, hunks, sidebyside));
+ }
+
+ void ButtonPressHandler(object o, ButtonPressEventArgs args) {
+ double position = ((double)args.Event.Y / Allocation.Height - (double)scroller.Allocation.Height/scroller.Vadjustment.Upper/2) * scroller.Vadjustment.Upper;
+ if (position < 0) position = 0;
+ if (position + scroller.Allocation.Height > scroller.Vadjustment.Upper) position = scroller.Vadjustment.Upper - scroller.Allocation.Height;
+ scroller.Vadjustment.Value = position;
+ }
+ }
+
+ class OverviewRenderer2 : DrawingArea {
+ Hunk[] hunks;
+ ScrolledWindow scroller;
+ bool sidebyside;
+
+ public OverviewRenderer2(ScrolledWindow scroller, Hunk[] hunks, bool sidebyside) {
+ this.hunks = hunks;
+ this.scroller = scroller;
+ this.sidebyside = sidebyside;
+ this.ExposeEvent += new ExposeEventHandler(OnExposed);
+ scroller.ExposeEvent += new ExposeEventHandler(OnScroll);
+ WidthRequest = 50;
+ }
+
+ void OnScroll (object o, ExposeEventArgs args) {
+ QueueDrawArea(0, 0, Allocation.Width, Allocation.Height);
+ }
+
+ void OnExposed (object o, ExposeEventArgs args) {
+ Gdk.GC gc = this.Style.BaseGC(StateType.Normal);
+
+ gc.Colormap.AllocColor(ref DiffWidget.ColorChanged, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorChangedHighlight, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorAdded, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorAddedHighlight, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorRemoved, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorRemovedHighlight, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorDefault, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorDefaultHighlight, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorBlack, false, true);
+ gc.Colormap.AllocColor(ref DiffWidget.ColorGrey, false, true);
+
+ int count = 0;
+ foreach (Hunk h in hunks) {
+ IncPos(h, ref count);
+ }
+
+ int start = 0;
+ foreach (Hunk h in hunks) {
+ int size = 0;
+ IncPos(h, ref size);
+
+ if (h.Same)
+ gc.Foreground = DiffWidget.ColorDefault;
+ else if (h.Original().Count == 0)
+ gc.Foreground = DiffWidget.ColorAdded;
+ else if (h.ChangedLists == 1 && h.Changes(0).Count == 0)
+ gc.Foreground = DiffWidget.ColorRemoved;
+ else
+ gc.Foreground = DiffWidget.ColorChanged;
+
+ GdkWindow.DrawRectangle(gc, true, 0, Allocation.Height*start/count, Allocation.Width, Allocation.Height*size/count);
+
+ start += size;
+ }
+
+ gc.Foreground = DiffWidget.ColorGrey;
+ GdkWindow.DrawRectangle(gc, false,
+ 1,
+ (int)(Allocation.Height*scroller.Vadjustment.Value/scroller.Vadjustment.Upper) + 1,
+ Allocation.Width-3,
+ (int)(Allocation.Height*((double)scroller.Allocation.Height/scroller.Vadjustment.Upper))-2);
+
+ gc.Foreground = DiffWidget.ColorBlack;
+ GdkWindow.DrawRectangle(gc, false,
+ 0,
+ (int)(Allocation.Height*scroller.Vadjustment.Value/scroller.Vadjustment.Upper),
+ Allocation.Width-1,
+ (int)(Allocation.Height*((double)scroller.Allocation.Height/scroller.Vadjustment.Upper)));
+
+ // Reset this otherwise MD colors get messed up.
+ // But what should I really do here?
+ gc.Foreground = DiffWidget.ColorDefault;
+ }
+
+ private void IncPos(Hunk h, ref int pos) {
+ if (sidebyside)
+ pos += h.MaxLines();
+ else if (h.Same)
+ pos += h.Original().Count;
+ else {
+ pos += h.Original().Count;
+ for (int i = 0; i < h.ChangedLists; i++)
+ pos += h.Changes(i).Count;
+ }
+ }
+ }
+ }
+}
Added: trunk/MonoDevelop/Extras/VersionControl/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Makefile.am 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Makefile.am 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,2 @@
+
+SUBDIRS = Diff DiffWidget VersionControl AddIn
Added: trunk/MonoDevelop/Extras/VersionControl/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/README 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/README 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,7 @@
+This is version control support for MD, including
+supporting libraries:
+ Diff: for finding differences between files
+ DiffWidget: for displaying differences in Gtk
+ VersionControl: abstraction over various systems
+ e.g. subversion
+The addin itself is in the AddIn directory.
Added: trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,19 @@
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+ASSEMBLY = $(ADDIN_BUILD)/VersionControl.dll
+
+DLLS =
+FILES = *.cs
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+
+$(ASSEMBLY): $(FILES)
+ mkdir -p $(ADDIN_BUILD)
+ $(CSC) $(CSC_FLAGS) $(FILES) -out:$@ -target:library
+
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES)
+
+all: $(ASSEMBLY)
+
+include $(top_srcdir)/Makefile.include
Added: trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,919 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using System.Runtime.InteropServices;
+
+namespace VersionControl {
+
+ public class SubversionVersionControl : VersionControlSystem {
+ SvnClient client;
+
+ SvnClient Client {
+ get {
+ if (client == null) client = new SvnClient();
+ return client;
+ }
+ }
+
+ string GetTextBase(string sourcefile) {
+ return Path.Combine(
+ Path.Combine(
+ Path.Combine(
+ Path.GetDirectoryName(sourcefile),
+ ".svn"),
+ "text-base"),
+ Path.GetFileName(sourcefile) + ".svn-base");
+ }
+
+ string GetDirectoryDotSvn(string sourcepath) {
+ return Path.Combine(sourcepath, ".svn");
+ }
+
+ public override bool IsDiffAvailable(string sourcefile) {
+ return File.Exists(GetTextBase(sourcefile));
+ }
+
+ public override bool IsHistoryAvailable(string sourcefile) {
+ return IsFileStatusAvailable(sourcefile) || IsDirectoryStatusAvailable(sourcefile);
+ }
+
+ public override bool IsFileStatusAvailable(string sourcefile) {
+ return IsDiffAvailable(sourcefile);
+ }
+
+ public override bool IsDirectoryStatusAvailable(string sourcepath) {
+ return Directory.Exists(GetDirectoryDotSvn(sourcepath));
+ }
+
+ public override bool CanUpdate(string sourcepath) {
+ return IsFileStatusAvailable(sourcepath)
+ || IsDirectoryStatusAvailable(sourcepath);
+ }
+
+ public override bool CanCommit(string sourcepath) {
+ return CanUpdate(sourcepath);
+ }
+
+ public override string GetPathToBaseText(string sourcefile) {
+ return GetTextBase(sourcefile);
+ }
+
+ public override RevisionDescription[] GetHistory(string sourcefile, RevisionPtr since) {
+ ArrayList revs = new ArrayList();
+ string curPath = Client.GetPathUrl(sourcefile);
+ if (curPath == null) return null;
+
+ SvnClient.Rev sincerev = SvnClient.Rev.First;
+ if (since != null)
+ sincerev = ((SvnRevisionPtr)since).ForApi();
+
+ foreach (SvnClient.LogEnt log in Client.Log(sourcefile, SvnClient.Rev.Head, sincerev)) {
+ RevisionDescription d = new RevisionDescription();
+ d.Author = log.Author;
+ d.Message = log.Message;
+ d.Revision = new SvnRevisionPtr(log.Revision);
+ d.RepositoryPath = curPath;
+ d.Time = log.Time;
+ revs.Add(d);
+
+ // Be aware of the change in path resulting from a copy
+ foreach (SvnClient.LogEntChangedPath chg in log.ChangedPaths) {
+ if (curPath.EndsWith(chg.Path) && chg.CopyFromPath != null) {
+ curPath = curPath.Substring(0, curPath.Length-chg.Path.Length) + chg.CopyFromPath;
+ }
+ }
+ }
+ return (RevisionDescription[])revs.ToArray(typeof(RevisionDescription));
+ }
+
+ public override string GetTextAtRevision(object repositoryPath, RevisionPtr revision) {
+ return Client.Cat(repositoryPath.ToString(), ((SvnRevisionPtr)revision).ForApi() );
+ }
+
+ public override Node GetFileStatus(string sourcefile, bool getRemoteStatus) {
+ IList statuses = Client.Status(sourcefile, SvnClient.Rev.Head, false, false, getRemoteStatus);
+ if (statuses.Count != 1)
+ throw new ArgumentException("Path does not refer to a file.");
+ SvnClient.StatusEnt ent = (SvnClient.StatusEnt)statuses[0];
+ if (ent.IsDirectory)
+ throw new ArgumentException("Path does not refer to a file.");
+ return CreateNode(ent, 0);
+ }
+
+ public override Node[] GetDirectoryStatus(string sourcepath, bool getRemoteStatus, bool recursive) {
+ ArrayList ret = new ArrayList();
+ string sourceurl = Client.GetPathUrl(sourcepath);
+ foreach (SvnClient.StatusEnt ent in Client.Status(sourcepath, SvnClient.Rev.Head, recursive, true, getRemoteStatus))
+ ret.Add( CreateNode(ent, sourceurl.Length+1) );
+ return (Node[])ret.ToArray(typeof(Node));
+ }
+
+ public override void Update(string path, bool recurse, UpdateCallback callback) {
+ Client.Update(path, recurse, callback);
+ }
+
+ public override void Commit(string[] paths, string message, UpdateCallback callback) {
+ Client.Commit(paths, message, callback);
+ }
+
+ private Node CreateNode(SvnClient.StatusEnt ent, int pathchop) {
+ Node ret = new Node();
+
+ ret.RepositoryPath = ent.Url;
+ ret.LocalRelativePath = ent.Url.Substring(pathchop);
+ ret.IsDirectory = ent.IsDirectory;
+ ret.BaseRevision = new SvnRevisionPtr(ent.Revision);
+ ret.Status = ConvertStatus(ent.Schedule, ent.TextStatus);
+
+ if (ent.RemoteTextStatus != SvnClient.NodeStatus.EMPTY) {
+ ret.RemoteStatus = ConvertStatus(SvnClient.NodeSchedule.Normal, ent.RemoteTextStatus);
+ ret.RemoteUpdate = new RevisionDescription();
+ ret.RemoteUpdate.RepositoryPath = ent.Url;
+ ret.RemoteUpdate.Revision = new SvnRevisionPtr(ent.LastCommitRevision);
+ ret.RemoteUpdate.Author = ent.LastCommitAuthor;
+ ret.RemoteUpdate.Message = "(unavailable)";
+ ret.RemoteUpdate.Time = ent.LastCommitDate;
+ }
+
+ return ret;
+ }
+
+ private NodeStatus ConvertStatus(SvnClient.NodeSchedule schedule, SvnClient.NodeStatus status) {
+ switch (schedule) {
+ case SvnClient.NodeSchedule.Add: return NodeStatus.ScheduledAdd;
+ case SvnClient.NodeSchedule.Delete: return NodeStatus.ScheduledDelete;
+ case SvnClient.NodeSchedule.Replace: return NodeStatus.ScheduledReplace;
+ }
+
+ switch (status) {
+ case SvnClient.NodeStatus.None: return NodeStatus.Unchanged;
+ case SvnClient.NodeStatus.Unversioned: return NodeStatus.Unversioned;
+ case SvnClient.NodeStatus.Modified: return NodeStatus.Modified;
+ case SvnClient.NodeStatus.Merged: return NodeStatus.Modified;
+ case SvnClient.NodeStatus.Conflicted: return NodeStatus.Conflicted;
+ case SvnClient.NodeStatus.Ignored: return NodeStatus.UnversionedIgnored;
+ case SvnClient.NodeStatus.Obstructed: return NodeStatus.Obstructed;
+ }
+ return NodeStatus.Unknown;
+ }
+
+ private class SvnRevisionPtr : RevisionPtr {
+ public readonly int Rev;
+ public SvnRevisionPtr(int rev) { Rev = rev; }
+
+ public override string ToString() {
+ return "Rev " + Rev;
+ }
+
+ public override RevisionPtr GetPrevious() {
+ return new SvnRevisionPtr(Rev-1);
+ }
+
+ public SvnClient.Rev ForApi() {
+ return SvnClient.Rev.Number(Rev);
+ }
+ }
+
+ }
+
+ public class SvnClient {
+ IntPtr pool;
+ IntPtr ctx;
+
+ object sync = new object();
+ bool inProgress = false;
+
+ UpdateCallback updatecallback;
+ string commitmessage = null;
+
+ // retain this so the delegates aren't GC'ed
+ svn_client_ctx_t ctxstruct;
+
+ static SvnClient() {
+ apr_initialize();
+ }
+
+ private IntPtr newpool(IntPtr parent) {
+ IntPtr p;
+ apr_pool_create_ex(out p, parent, IntPtr.Zero, IntPtr.Zero);
+ if (p == IntPtr.Zero)
+ throw new InvalidOperationException("Could not create an APR pool.");
+ return p;
+ }
+
+ public SvnClient() {
+ // Allocate the APR pool and the SVN client context.
+
+ pool = newpool(IntPtr.Zero);
+
+ if (svn_client_create_context(out ctx, pool) != IntPtr.Zero)
+ throw new InvalidOperationException("Could not create a Subversion client context.");
+
+ // Set the callbacks on the client context structure.
+
+ // This is quite a roudabout way of doing this. The task
+ // is to set the notify field of the unmanaged structure
+ // at the address 'ctx' -- with the address of a delegate.
+ // There's no way to get an address for the delegate directly,
+ // as far as I could figure out, so instead I create a managed
+ // structure that mirrors the start of the unmanaged structure
+ // I want to modify. Then I marshal the managed structure
+ // *onto* to unmanaged one, overwriting fields in the process.
+ // I don't use references to the structure itself in the API
+ // calls because it needs to be allocated by SVN. I hope
+ // this doesn't cause any memory leaks.
+ ctxstruct = new svn_client_ctx_t();
+ ctxstruct.NotifyFunc = new svn_wc_notify_func_t(svn_wc_notify_func_t_impl);
+ ctxstruct.LogMsgFunc = new svn_client_get_commit_log_t(svn_client_get_commit_log_impl);
+ Marshal.StructureToPtr(ctxstruct, ctx, false);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct svn_client_ctx_t {
+ public IntPtr auth_baton;
+ public svn_wc_notify_func_t NotifyFunc;
+ public IntPtr notify_baton;
+ public svn_client_get_commit_log_t LogMsgFunc;
+ public IntPtr logmsg_baton;
+ }
+
+ ~SvnClient() {
+ apr_pool_destroy(pool);
+ }
+
+ // Wrappers for native interop
+
+ public string Version {
+ get {
+ IntPtr ptr = svn_client_version();
+ svn_version_t ver = (svn_version_t)Marshal.PtrToStructure(ptr, typeof(svn_version_t));
+ return ver.major + "." + ver.minor + "." + ver.patch;
+ }
+ }
+
+ public IList List(string pathorurl, bool recurse, Rev revision) {
+ if (pathorurl == null) throw new ArgumentException();
+
+ IntPtr localpool = newpool(pool);
+ ArrayList items = new ArrayList();
+ try {
+ IntPtr hash;
+
+ CheckError(svn_client_ls(out hash, pathorurl, ref revision,
+ recurse ? 1 : 0, ctx, localpool));
+
+ IntPtr item = apr_hash_first(localpool, hash);
+ while (item != IntPtr.Zero) {
+ IntPtr nameptr, val;
+ int namelen;
+ apr_hash_this(item, out nameptr, out namelen, out val);
+
+ string name = Marshal.PtrToStringAnsi(nameptr);
+ svn_dirent_t ent = (svn_dirent_t)Marshal.PtrToStructure(val, typeof(svn_dirent_t));
+ item = apr_hash_next(item);
+
+ items.Add(new DirEnt(name, ent));
+ }
+
+ } finally {
+ apr_pool_destroy(localpool);
+ }
+ return items;
+ }
+
+ public IList Status(string path, Rev revision) {
+ return Status(path, revision, false, false, false);
+ }
+
+ public IList Status(string path, Rev revision, bool descendDirs, bool changedItemsOnly, bool remoteStatus) {
+ if (path == null) throw new ArgumentException();
+
+ ArrayList ret = new ArrayList();
+ int result_rev = 0;
+
+ StatusCollector collector = new StatusCollector(ret);
+ CheckError(svn_client_status (ref result_rev, path, ref revision,
+ new svn_wc_status_func_t(collector.Func),
+ IntPtr.Zero,
+ descendDirs ? 1 : 0, changedItemsOnly ? 0 : 1, remoteStatus ? 1 : 0, 1,
+ ctx, pool));
+
+ return ret;
+ }
+
+ public IList Log(string path, Rev revisionStart, Rev revisionEnd) {
+ if (path == null) throw new ArgumentException();
+
+ ArrayList ret = new ArrayList();
+ IntPtr localpool = newpool(pool);
+ IntPtr strptr = IntPtr.Zero;
+ try {
+ IntPtr array = apr_array_make(localpool, 0, IntPtr.Size);
+ IntPtr first = apr_array_push(array);
+ strptr = Marshal.StringToHGlobalAnsi(path);
+ Marshal.WriteIntPtr(first, strptr);
+
+ LogCollector collector = new LogCollector(ret);
+
+ CheckError(svn_client_log (
+ array,
+ ref revisionStart, ref revisionEnd,
+ 1,
+ 0,
+ new svn_log_message_receiver_t(collector.Func),
+ IntPtr.Zero,
+ ctx, localpool));
+ } finally {
+ if (strptr != IntPtr.Zero)
+ Marshal.FreeHGlobal(strptr);
+ apr_pool_destroy(localpool);
+ }
+ return ret;
+ }
+
+ public string GetPathUrl(string path) {
+ if (path == null) throw new ArgumentException();
+
+ IntPtr ret = IntPtr.Zero;
+ CheckError(svn_client_url_from_path(ref ret, path, pool));
+ if (ret == IntPtr.Zero) return null;
+ return Marshal.PtrToStringAnsi(ret);
+ }
+
+ public string Cat(string pathorurl, Rev revision) {
+ MemoryStream memstream = new MemoryStream();
+ Cat(pathorurl, revision, memstream);
+ try {
+ return System.Text.Encoding.UTF8.GetString(memstream.GetBuffer());
+ } catch (Exception e) {
+ }
+ return System.Text.Encoding.ASCII.GetString(memstream.GetBuffer());
+ }
+
+ public void Cat(string pathorurl, Rev revision, Stream stream) {
+ if (pathorurl == null) throw new ArgumentException();
+ if (stream == null) throw new ArgumentException();
+
+ IntPtr localpool = newpool(pool);
+ try {
+ StreamCollector collector = new StreamCollector(stream);
+ IntPtr svnstream = svn_stream_create(IntPtr.Zero, localpool);
+ svn_stream_set_write(svnstream, new svn_readwrite_fn_t(collector.Func));
+ CheckError(svn_client_cat(svnstream, pathorurl, ref revision, ctx, localpool));
+ } finally {
+ apr_pool_destroy(localpool);
+ }
+ }
+
+ public void Update(string path, bool recurse, UpdateCallback callback) {
+ if (path == null || callback == null) throw new ArgumentException();
+
+ lock (sync) {
+ if (inProgress)
+ throw new SubversionException("Another Subversion operation is already in progress.");
+ inProgress = true;
+ }
+
+ updatecallback = callback;
+ int result_rev;
+ Rev rev = Rev.Head;
+ IntPtr localpool = newpool(pool);
+ try {
+ CheckError(svn_client_update(out result_rev, path, ref rev, recurse ? 1 : 0, ctx, localpool));
+ } finally {
+ apr_pool_destroy(localpool);
+ updatecallback = null;
+ inProgress = false;
+ }
+ }
+
+ public void Commit(string[] paths, string message, UpdateCallback callback) {
+ if (paths == null || message == null || callback == null)
+ throw new ArgumentNullException();
+
+ lock (sync) {
+ if (inProgress) throw new SubversionException("Another Subversion operation is already in progress.");
+ inProgress = true;
+ }
+
+ updatecallback = callback;
+
+ IntPtr localpool = newpool(pool);
+ try {
+ // Put each item into an APR array.
+ IntPtr array = apr_array_make(localpool, 0, IntPtr.Size);
+ foreach (string path in paths) {
+ IntPtr item = apr_array_push(array);
+ Marshal.WriteIntPtr(item, apr_pstrdup(localpool, path));
+ }
+
+ IntPtr commit_info = IntPtr.Zero;
+
+ commitmessage = message;
+
+ CheckError(svn_client_commit (
+ ref commit_info, array,
+ 0, ctx, localpool));
+ } finally {
+ commitmessage = null;
+ updatecallback = null;
+ apr_pool_destroy(localpool);
+ inProgress = false;
+ }
+ }
+
+ IntPtr svn_client_get_commit_log_impl(ref IntPtr log_msg,
+ ref IntPtr tmp_file, IntPtr commit_items, IntPtr baton,
+ IntPtr pool) {
+ log_msg = apr_pstrdup(pool, commitmessage);
+ tmp_file = IntPtr.Zero;
+ return IntPtr.Zero;
+ }
+
+ private void CheckError(IntPtr error) {
+ if (error == IntPtr.Zero) return;
+ svn_error_t error_t = (svn_error_t)Marshal.PtrToStructure(error, typeof(svn_error_t));
+ throw new SubversionException(error_t.message);
+ }
+
+ void svn_wc_notify_func_t_impl(IntPtr baton, IntPtr path,
+ NotifyAction action, NodeKind kind, IntPtr mime_type,
+ NotifyState content_state, NotifyState prop_state, long revision) {
+ string actiondesc = action.ToString();
+ switch (action) {
+ case NotifyAction.UpdateAdd: actiondesc = "Added"; break;
+ case NotifyAction.UpdateDelete: actiondesc = "Deleted"; break;
+ case NotifyAction.UpdateUpdate: actiondesc = "Updating"; break;
+ case NotifyAction.UpdateExternal: actiondesc = "External Updated"; break;
+ case NotifyAction.UpdateCompleted: actiondesc = "Finished"; break;
+
+ case NotifyAction.CommitAdded: actiondesc = "Added"; break;
+ case NotifyAction.CommitDeleted: actiondesc = "Deleted"; break;
+ case NotifyAction.CommitModified: actiondesc = "Modified"; break;
+ case NotifyAction.CommitReplaced: actiondesc = "Replaced"; break;
+ case NotifyAction.CommitPostfixTxDelta: actiondesc = "Sending Content"; break;
+ /*Add,
+ Copy,
+ Delete,
+ Restore,
+ Revert,
+ FailedRevert,
+ Resolved,
+ Skip,
+ StatusCompleted,
+ StatusExternal,
+ BlameRevision*/
+ }
+
+ if (updatecallback != null)
+ updatecallback(Marshal.PtrToStringAnsi(path), actiondesc);
+ }
+
+ private class StatusCollector {
+ ArrayList statuses;
+ public StatusCollector(ArrayList statuses) { this.statuses = statuses; }
+ public void Func(IntPtr baton, IntPtr path, ref svn_wc_status_t status) {
+ if (status.to__svn_wc_entry_t == IntPtr.Zero)
+ return;
+ statuses.Add(new StatusEnt(status));
+ }
+
+ }
+
+ private class LogCollector {
+ static readonly DateTime Epoch = new DateTime(1970,1,1);
+
+ ArrayList logs;
+ public LogCollector(ArrayList logs) { this.logs = logs; }
+ public IntPtr Func(IntPtr baton, IntPtr apr_hash_changed_paths, int revision, IntPtr author, IntPtr date, IntPtr message, IntPtr pool) {
+ long time;
+ svn_time_from_cstring(out time, Marshal.PtrToStringAnsi(date), pool);
+ string smessage = "";
+ if (message != IntPtr.Zero) smessage = Marshal.PtrToStringAnsi(message).Trim();
+
+ ArrayList items = new ArrayList();
+
+ IntPtr item = apr_hash_first(pool, apr_hash_changed_paths);
+ while (item != IntPtr.Zero) {
+ IntPtr nameptr, val;
+ int namelen;
+ apr_hash_this(item, out nameptr, out namelen, out val);
+
+ string name = Marshal.PtrToStringAnsi(nameptr);
+ svn_log_changed_path_t ch = (svn_log_changed_path_t)Marshal.PtrToStructure(val, typeof(svn_log_changed_path_t));
+ item = apr_hash_next(item);
+
+ items.Add(new LogEntChangedPath(name, ch));
+ }
+
+ logs.Add(new LogEnt(revision, Marshal.PtrToStringAnsi(author), Epoch.AddTicks(time*10), smessage,
+ (LogEntChangedPath[])items.ToArray(typeof(LogEntChangedPath))));
+
+ return IntPtr.Zero;
+ }
+ }
+
+ private class StreamCollector {
+ Stream buf;
+ public StreamCollector(Stream buf) { this.buf = buf; }
+ public IntPtr Func(IntPtr baton, IntPtr data, ref int len) {
+ Console.WriteLine(len);
+ for (int i = 0; i < len; i++)
+ buf.WriteByte(Marshal.ReadByte((IntPtr)((int)data+i)));
+ return IntPtr.Zero;
+ }
+ }
+
+ public class DirEnt {
+ public readonly string Name;
+ public readonly bool IsDirectory;
+ public readonly long Size;
+ public readonly bool HasProps;
+ public readonly int CreatedRevision;
+ public readonly DateTime Time;
+ public readonly string LastAuthor;
+
+ static readonly DateTime Epoch = new DateTime(1970,1,1);
+
+ internal DirEnt(string name, svn_dirent_t ent) {
+ Name = name;
+ IsDirectory = (ent.kind == (int)NodeKind.Dir);
+ Size = ent.size;
+ HasProps = ent.has_props != 0;
+ CreatedRevision = ent.created_rev;
+ Time = Epoch.AddTicks(ent.time*10);
+ LastAuthor = ent.last_author;
+ }
+
+ }
+
+ public class StatusEnt {
+ public readonly string Name;
+ public readonly int Revision;
+ public readonly string Url;
+ public readonly string Repository;
+ public readonly string RepositoryUuid;
+ public readonly bool IsDirectory;
+ public readonly NodeSchedule Schedule;
+ public readonly bool Copied;
+ public readonly bool Deleted;
+ public readonly bool Absent;
+ public readonly bool Incomplete;
+ public readonly string CopiedFrom;
+ public readonly int CopiedFromRevision;
+ public readonly string ConflictOld;
+ public readonly string ConflictNew;
+ public readonly string ConflictWorking;
+ public readonly string PropertyRejectFile;
+ public readonly DateTime TextTime;
+ public readonly DateTime PropTime;
+ public readonly string Checksum; //(base64 for text-base, or NULL);
+ public readonly int LastCommitRevision;
+ public readonly DateTime LastCommitDate;
+ public readonly string LastCommitAuthor;
+
+ public readonly NodeStatus TextStatus;
+ public readonly NodeStatus PropsStatus;
+ public readonly bool Locked;
+ public readonly bool Switched;
+ public readonly NodeStatus RemoteTextStatus;
+ public readonly NodeStatus RemotePropsStatus;
+
+ static readonly DateTime Epoch = new DateTime(1970,1,1);
+
+ internal StatusEnt(svn_wc_status_t status) {
+ TextStatus = (NodeStatus)status.svn_wc_status_kind__text;
+ PropsStatus = (NodeStatus)status.svn_wc_status_kind__props;
+ Locked = status.locked != 0;
+ Copied = status.copied != 0;
+ Switched = status.switched != 0;
+ RemoteTextStatus = (NodeStatus)status.svn_wc_status_kind__text__repo;
+ RemotePropsStatus = (NodeStatus)status.svn_wc_status_kind__props__repo;
+
+ if (status.to__svn_wc_entry_t == IntPtr.Zero)
+ return;
+
+ svn_wc_entry_t ent = (svn_wc_entry_t)Marshal.PtrToStructure(status.to__svn_wc_entry_t, typeof(svn_wc_entry_t));
+ Name = ent.name;
+ Revision = ent.revision;
+ Url = ent.url;
+ Repository = ent.repos;
+ RepositoryUuid = ent.repos_uuid;
+ IsDirectory = (ent.node_kind == (int)NodeKind.Dir);
+ Schedule = (NodeSchedule)ent.schedule;
+ Copied = ent.copied != 0;
+ Deleted = ent.deleted != 0;
+ Absent = ent.absent != 0;
+ Incomplete = ent.incomplete != 0;
+ CopiedFrom = ent.copied_from;
+ CopiedFromRevision = ent.copied_rev;
+ ConflictOld = ent.conflict_old;
+ ConflictNew = ent.conflict_new;
+ ConflictWorking = ent.conflict_working;
+ PropertyRejectFile = ent.property_reject_file;
+ TextTime = Epoch.AddTicks(ent.text_time*10);
+ PropTime = Epoch.AddTicks(ent.prop_time*10);
+ Checksum = ent.checksum;
+ LastCommitRevision = ent.last_commit_rev;
+ LastCommitDate = Epoch.AddTicks(ent.last_commit_date*10);
+ LastCommitAuthor = ent.last_commit_author;
+ }
+
+ }
+
+ public class LogEnt {
+ public readonly int Revision;
+ public readonly string Author;
+ public readonly DateTime Time;
+ public readonly string Message;
+ public readonly LogEntChangedPath[] ChangedPaths;
+
+ internal LogEnt(int rev, string author, DateTime time, string msg, LogEntChangedPath[] changes) {
+ Revision = rev;
+ Author = author;
+ Time = time;
+ Message = msg;
+ ChangedPaths = changes;
+ }
+ }
+
+ public class LogEntChangedPath {
+ public readonly string Path;
+ public readonly ActionType Action;
+ public readonly string CopyFromPath;
+ public readonly int CopyFromRevision;
+
+ internal LogEntChangedPath(string path, svn_log_changed_path_t info) {
+ Path = path;
+ CopyFromPath = info.copy_from_path;
+ CopyFromRevision = info.copy_from_rev;
+
+ switch (info.action) {
+ case 'A': Action = ActionType.Add; break;
+ case 'D': Action = ActionType.Delete; break;
+ case 'R': Action = ActionType.Replace; break;
+ default: Action = ActionType.Modify; break;
+ }
+ }
+
+ public enum ActionType {
+ Add,
+ Delete,
+ Replace,
+ Modify
+ }
+ }
+
+ // Native Interop
+
+ private const string aprlib = "libapr-0.so.0";
+
+ [DllImport(aprlib)] static extern void apr_initialize();
+
+ [DllImport(aprlib)] static extern void apr_pool_create_ex(out IntPtr pool, IntPtr parent, IntPtr abort, IntPtr allocator);
+
+ [DllImport(aprlib)] static extern void apr_pool_destroy(IntPtr pool);
+
+ [DllImport(aprlib)] static extern IntPtr apr_hash_first(IntPtr pool, IntPtr hash);
+
+ [DllImport(aprlib)] static extern IntPtr apr_hash_next(IntPtr hashindex);
+
+ [DllImport(aprlib)] static extern void apr_hash_this(IntPtr hashindex, out IntPtr key, out int keylen, out IntPtr val);
+
+ [DllImport(aprlib)] static extern IntPtr apr_array_make(IntPtr pool, int nelts, int elt_size);
+
+ [DllImport(aprlib)] static extern IntPtr apr_array_push(IntPtr arr);
+
+ [DllImport(aprlib)] static extern IntPtr apr_pstrdup(IntPtr pool, string s);
+
+ private const string svnclientlib = "libsvn_client-1.so.0";
+
+ private struct svn_error_t {
+ public int apr_err;
+ public string message;
+ public IntPtr svn_error_t__child;
+ public IntPtr pool;
+ }
+
+ private struct svn_version_t {
+ public int major;
+ public int minor;
+ public int patch;
+ public string tag;
+ }
+
+ enum NodeKind {
+ None,
+ File,
+ Dir,
+ Unknown
+ }
+
+ internal struct svn_dirent_t {
+ public int kind;
+ public long size;
+ public int has_props;
+ public int created_rev;
+ public long time; // created
+ public string last_author;
+ }
+
+ public enum NodeSchedule {
+ Normal,
+ Add,
+ Delete,
+ Replace
+ }
+
+ public enum NodeStatus {
+ EMPTY,
+ None,
+ Unversioned,
+ Normal,
+ Added,
+ Missing,
+ Deleted,
+ Replaced,
+ Modified,
+ Merged,
+ Conflicted,
+ Ignored,
+ Obstructed,
+ External,
+ Incomplete
+ }
+
+ internal struct svn_wc_entry_t {
+ public string name;
+ public int revision;
+ public string url;
+ public string repos;
+ public string repos_uuid;
+ public int node_kind;
+ public int schedule;
+ public int copied;
+ public int deleted;
+ public int absent;
+ public int incomplete;
+ public string copied_from;
+ public int copied_rev;
+ public string conflict_old;
+ public string conflict_new;
+ public string conflict_working;
+ public string property_reject_file;
+ public long text_time; // or zero
+ public long prop_time;
+ public string checksum; //(base64 for text-base, or NULL);
+ public int last_commit_rev;
+ public long last_commit_date;
+ public string last_commit_author;
+ }
+
+ internal struct svn_wc_status_t {
+ public IntPtr to__svn_wc_entry_t;
+ public int svn_wc_status_kind__text;
+ public int svn_wc_status_kind__props;
+ public int locked;
+ public int copied;
+ public int switched;
+ public int svn_wc_status_kind__text__repo;
+ public int svn_wc_status_kind__props__repo;
+ }
+
+ public struct Rev {
+ public int kind;
+ public int number;
+
+ public Rev(int kind, int number) {
+ this.kind = kind;
+ this.number = number;
+ }
+
+ public static Rev Number(int rev) { return new Rev(1, rev); }
+
+ public readonly static Rev Blank = new Rev(0, 0);
+ public readonly static Rev First = new Rev(1, 1);
+ public readonly static Rev Committed = new Rev(3, 0);
+ public readonly static Rev Previous = new Rev(4, 0);
+ public readonly static Rev Base = new Rev(5, 0);
+ public readonly static Rev Working = new Rev(6, 0);
+ public readonly static Rev Head = new Rev(7, 0);
+ }
+
+ internal struct svn_log_changed_path_t {
+ public char action;
+ public string copy_from_path;
+ public int copy_from_rev;
+ }
+
+ internal enum NotifyAction {
+ Add,
+ Copy,
+ Delete,
+ Restore,
+ Revert,
+ FailedRevert,
+ Resolved,
+ Skip,
+ UpdateDelete,
+ UpdateAdd,
+ UpdateUpdate,
+ UpdateCompleted,
+ UpdateExternal,
+ StatusCompleted,
+ StatusExternal,
+ CommitModified,
+ CommitAdded,
+ CommitDeleted,
+ CommitReplaced,
+ CommitPostfixTxDelta,
+ BlameRevision
+ }
+
+ internal enum NotifyState {
+ Inapplicable,
+ Unknown,
+ Unchanged,
+ Missing,
+ Obstructed,
+ Changed,
+ Merged,
+ Conflicted
+ }
+
+ delegate void svn_wc_status_func_t(IntPtr baton, IntPtr path,
+ ref svn_wc_status_t status);
+
+ delegate IntPtr svn_log_message_receiver_t(IntPtr baton,
+ IntPtr apr_hash_changed_paths, int revision, IntPtr author,
+ IntPtr date, IntPtr message, IntPtr pool);
+
+ delegate IntPtr svn_readwrite_fn_t(IntPtr baton, IntPtr data, ref int len);
+
+ delegate void svn_wc_notify_func_t(IntPtr baton, IntPtr path,
+ NotifyAction action, NodeKind kind, IntPtr mime_type,
+ NotifyState content_state, NotifyState prop_state, long revision);
+
+ delegate IntPtr svn_client_get_commit_log_t(ref IntPtr log_msg,
+ ref IntPtr tmp_file, IntPtr commit_items, IntPtr baton,
+ IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_version();
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_create_context(out IntPtr ctx, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_ls (
+ out IntPtr dirents, string path_or_url, ref Rev revision,
+ int recurse, IntPtr ctx, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_status (
+ ref int result_rev, string path, ref Rev revision,
+ svn_wc_status_func_t status_func, IntPtr status_baton,
+ int descend, int get_all, int update, int no_ignore,
+ IntPtr ctx, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_log (
+ IntPtr apr_array_header_t__targets,
+ ref Rev rev_start, ref Rev rev_end,
+ int discover_changed_paths,
+ int strict_node_history,
+ svn_log_message_receiver_t receiver,
+ IntPtr receiver_baton,
+ IntPtr ctx, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_time_from_cstring (
+ out long aprtime, string time, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_url_from_path (
+ ref IntPtr url, string path_or_url, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_cat (
+ IntPtr stream, string path_or_url,
+ ref Rev revision,
+ IntPtr ctx, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_stream_create (
+ IntPtr baton, IntPtr pool);
+
+ //[DllImport(svnclientlib)] static extern IntPtr svn_stream_set_read (
+ // IntPtr stream, svn_readwrite_fn_t reader);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_stream_set_write (
+ IntPtr stream, svn_readwrite_fn_t writer);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_update (
+ out int result_rev,
+ string path, ref Rev revision,
+ int recurse, IntPtr ctx, IntPtr pool);
+
+ [DllImport(svnclientlib)] static extern IntPtr svn_client_commit (
+ ref IntPtr svn_client_commit_info_t__commit_info,
+ IntPtr apr_array_header_t__targets, int nonrecursive,
+ IntPtr ctx, IntPtr pool);
+ }
+
+ public class SubversionException : ApplicationException {
+ public SubversionException(string message) : base(message) { }
+ }
+}
Added: trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs 2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,63 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace VersionControl {
+ public abstract class VersionControlSystem {
+ public abstract bool IsDiffAvailable(string sourcefile);
+ public abstract bool IsHistoryAvailable(string sourcefile);
+ public abstract bool IsFileStatusAvailable(string sourcefile);
+ public abstract bool IsDirectoryStatusAvailable(string sourcepath);
+ public abstract bool CanUpdate(string sourcepath);
+ public abstract bool CanCommit(string sourcepath);
+
+ public abstract string GetPathToBaseText(string sourcefile);
+ public abstract string GetTextAtRevision(object repositoryPath, RevisionPtr revision);
+ public abstract RevisionDescription[] GetHistory(string sourcefile, RevisionPtr since);
+ public abstract Node GetFileStatus(string sourcefile, bool getRemoteStatus);
+ public abstract Node[] GetDirectoryStatus(string sourcepath, bool getRemoteStatus, bool recursive);
+ public abstract void Update(string path, bool recurse, UpdateCallback callback);
+ public abstract void Commit(string[] paths, string message, UpdateCallback callback);
+ }
+
+ public delegate void UpdateCallback(string path, string action);
+
+ public class RevisionDescription {
+ public object RepositoryPath;
+ public RevisionPtr Revision;
+ public DateTime Time;
+ public string Author;
+ public string Message;
+ }
+
+ public abstract class RevisionPtr {
+ public abstract RevisionPtr GetPrevious();
+ }
+
+ public class Node {
+ public string LocalRelativePath;
+ public object RepositoryPath;
+ public bool IsDirectory;
+
+ public NodeStatus Status = NodeStatus.Unknown;
+ public RevisionPtr BaseRevision;
+
+ public NodeStatus RemoteStatus = NodeStatus.Unknown;
+ public RevisionDescription RemoteUpdate;
+ }
+
+ public enum NodeStatus {
+ Unknown,
+ Unversioned,
+ UnversionedIgnored,
+ Missing,
+ Obstructed,
+ Unchanged,
+ Modified,
+ ScheduledAdd,
+ ScheduledDelete,
+ ScheduledReplace,
+ Conflicted
+ }
+
+}
Modified: trunk/MonoDevelop/configure.in
===================================================================
--- trunk/MonoDevelop/configure.in 2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/configure.in 2005-06-22 19:12:34 UTC (rev 2604)
@@ -227,6 +227,11 @@
Extras/MonoDeveloperExtensions/Makefile
Extras/JavaBinding/Makefile
Extras/BooBinding/Makefile
+Extras/VersionControl/Makefile
+Extras/VersionControl/Diff/Makefile
+Extras/VersionControl/DiffWidget/Makefile
+Extras/VersionControl/VersionControl/Makefile
+Extras/VersionControl/AddIn/Makefile
contrib/Makefile
])
More information about the Monodevelop-patches-list
mailing list