From 2df1b8944e871d8c0ed9d973850cac90a03e2b9e Mon Sep 17 00:00:00 2001
From: mrward <ward.matt@gmail.com>
Date: Wed, 1 Sep 2010 15:19:06 +0100
Subject: [PATCH] Can now send the current text editor line to the Ruby Console
 window.

---
 data/resources/StringResources.nl.resx        |   6 +
 data/resources/StringResources.resx           |   3 +
 .../BackendBindings/Ruby/RubyBinding.sln      |   2 +-
 .../RubyBinding/Project/RubyBinding.addin     |  24 ++-
 .../RubyBinding/Project/RubyBinding.csproj    |   8 +
 .../RubyBinding/Project/Src/AddInOptions.cs   |  85 --------
 .../Project/Src/ConvertToRubyMenuCommand.cs   |   4 +-
 .../Project/Src/IConsoleTextEditor.cs         |   4 +-
 .../Ruby/RubyBinding/Project/Src/ILock.cs     |  16 ++
 .../RubyBinding/Project/Src/IRubyConsole.cs   |  16 ++
 .../Project/Src/IRubyConsolePad.cs            |  18 ++
 .../RubyBinding/Project/Src/IRubyWorkbench.cs |  18 ++
 .../RubyBinding/Project/Src/RubyConsole.cs    | 115 +++++++---
 .../Project/Src/RubyConsoleHost.cs            |  10 +-
 .../RubyBinding/Project/Src/RubyConsolePad.cs |  18 +-
 .../Project/Src/RubyConsoleTextEditor.cs      |  58 ++---
 .../Project/Src/RubyTextEditorViewContent.cs  |   4 +-
 .../RubyBinding/Project/Src/RubyWorkbench.cs  |  33 +++
 .../Project/Src/RunDebugRubyCommand.cs        |   4 +-
 .../RubyBinding/Project/Src/RunRubyCommand.cs |   8 +-
 .../Src/SendLineToRubyConsoleCommand.cs       |  73 +++++++
 .../RubyBinding/Project/Src/StringListLock.cs |  39 ++++
 .../Src/ThreadSafeRubyConsoleTextEditor.cs    | 117 ++++++++++
 .../Console/DisposedRubyConsoleTestFixture.cs |  12 +-
 ...ssedWhenCompletionWindowOpenTestFixture.cs |  25 +--
 .../RubyConsoleCodeCompletionTestFixture.cs   |  21 +-
 ...ubyConsoleCommandLineHistoryTestFixture.cs |  49 ++---
 .../RubyConsoleCurrentLineTestFixture.cs      |   6 +-
 .../Console/RubyConsoleEnterKeyTestFixture.cs |  35 ++-
 .../Console/RubyConsoleHomeKeyTestFixture.cs  |  19 +-
 .../RubyConsoleReadOnlyRegionsTestFixture.cs  | 117 +++++-----
 .../Console/RubyConsoleReadTestFixture.cs     |  33 ++-
 .../Test/Console/RubyConsoleSendLineTests.cs  | 174 +++++++++++++++
 .../Test/Console/RubyConsoleTestsBase.cs      |  25 +++
 ...xture.cs => RubyConsoleTextEditorTests.cs} |   2 +-
 .../RubyConsoleUnreadLinesTestFixture.cs      |  21 +-
 .../Console/RubyConsoleWriteTestFixture.cs    |  25 +--
 ...> ThreadSafeRubyConsoleTextEditorTests.cs} |  14 +-
 .../TwoRubyConsoleLinesWaitingTestFixture.cs  |   7 +-
 ...nvertCSharpToRubyMenuCommandTestFixture.cs |  18 +-
 ...onvertVBNetToRubyMenuCommandTestFixture.cs |  21 +-
 .../Gui/DebugRunRubyCommandTestFixture.cs     |   9 +-
 ...dTestFixture.cs => RunRubyCommandTests.cs} |  11 +-
 .../Gui/SendLineToRubyConsoleCommandTests.cs  |  93 ++++++++
 .../Test/LanguageBindingTestFixture.cs        |  54 -----
 .../RubyBinding/Test/RubyBinding.Tests.csproj |  17 +-
 .../Ruby/RubyBinding/Test/Utils/FakeCaret.cs  |  53 +++++
 .../RubyBinding/Test/Utils/FakeDocument.cs    | 139 ++++++++++++
 .../Test/Utils/FakeDocumentLine.cs            |  23 ++
 .../Ruby/RubyBinding/Test/Utils/FakeLock.cs   |  33 +++
 .../Test/Utils/MockConsoleTextEditor.cs       | 199 ++++++++----------
 .../Test/Utils/MockEditableViewContent.cs     |  21 +-
 .../RubyBinding/Test/Utils/MockRubyConsole.cs |  22 ++
 .../Test/Utils/MockRubyConsolePad.cs          |  33 +++
 .../RubyBinding/Test/Utils/MockTextEditor.cs  |  18 +-
 .../RubyBinding/Test/Utils/MockWorkbench.cs   | 171 ++-------------
 .../Test/Utils/MockWorkbenchWindow.cs         | 129 ------------
 .../Test/Utils/TestableRubyConsole.cs         |  56 +++++
 .../MockEditableViewContentTestFixture.cs     |   2 +-
 59 files changed, 1473 insertions(+), 917 deletions(-)
 delete mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/AddInOptions.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ILock.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsole.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsolePad.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyWorkbench.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyWorkbench.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/SendLineToRubyConsoleCommand.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/StringListLock.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ThreadSafeRubyConsoleTextEditor.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleSendLineTests.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTestsBase.cs
 rename src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/{ConsoleTextEditorTestFixture.cs => RubyConsoleTextEditorTests.cs} (99%)
 rename src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/{ConsoleTextEditorThreadSafetyTestFixture.cs => ThreadSafeRubyConsoleTextEditorTests.cs} (90%)
 rename src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/{RunRubyCommandTestFixture.cs => RunRubyCommandTests.cs} (78%)
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/SendLineToRubyConsoleCommandTests.cs
 delete mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/LanguageBindingTestFixture.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeCaret.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocument.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocumentLine.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeLock.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsole.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsolePad.cs
 delete mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbenchWindow.cs
 create mode 100644 src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/TestableRubyConsole.cs

diff --git a/data/resources/StringResources.nl.resx b/data/resources/StringResources.nl.resx
index b3998b391e..3eb683f9de 100644
--- a/data/resources/StringResources.nl.resx
+++ b/data/resources/StringResources.nl.resx
@@ -4059,9 +4059,15 @@ Configureer a.u.b. in SharpDevelop opties de locatie van NAnt's uitvoerend besta
   <data name="ICSharpCode.PythonBinding.PythonProjectFiles" xml:space="preserve">
     <value>Python projectbestanden</value>
   </data>
+  <data name="ICSharpCode.PythonBinding.SendLineToPythonConsole" xml:space="preserve">
+    <value>Stuur regel naar Python console</value>
+  </data>
   <data name="ICSharpCode.PythonBinding.UnknownTypeName" xml:space="preserve">
     <value>Kan type {0} niet vinden. Ontbreekt er een assembly referentie?</value>
   </data>
+  <data name="ICSharpCode.RubyBinding.SendLineToRubyConsole" xml:space="preserve">
+    <value>Stuur regel naar Ruby console</value>
+  </data>
   <data name="ICSharpCode.Services.FileUtilityService.CantLoadFileStandardText" xml:space="preserve">
     <value>Kan het bestand ${FileNameWithoutPath} in de map ${Path} niet laden. Controleer de beveiliging en het bestaan van het bestand.</value>
   </data>
diff --git a/data/resources/StringResources.resx b/data/resources/StringResources.resx
index 012d0e5c89..def2b77f69 100644
--- a/data/resources/StringResources.resx
+++ b/data/resources/StringResources.resx
@@ -4071,6 +4071,9 @@ Please configure the NAnt executable's location in the SharpDevelop Options.</va
   <data name="ICSharpCode.PythonBinding.UnknownTypeName" xml:space="preserve">
     <value>Could not find type '{0}'. Are you missing an assembly reference?</value>
   </data>
+  <data name="ICSharpCode.RubyBinding.SendLineToRubyConsole" xml:space="preserve">
+    <value>Send Line to Ruby Console</value>
+  </data>
   <data name="ICSharpCode.Services.FileUtilityService.CantLoadFileStandardText" xml:space="preserve">
     <value>Can't load file ${FileNameWithoutPath} under ${Path}.
 Check the file permission and the existence of that file.</value>
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding.sln b/src/AddIns/BackendBindings/Ruby/RubyBinding.sln
index c59bc1eb3b..c99709c242 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding.sln
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 11.00
 # Visual Studio 2010
-# SharpDevelop 4.0.0.5967
+# SharpDevelop 4.0.0.6500
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RubyBinding", "RubyBinding\Project\RubyBinding.csproj", "{C896FFFF-5B6C-4B0E-B6DF-049865F501B4}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RubyBinding.Tests", "RubyBinding\Test\RubyBinding.Tests.csproj", "{01DF0475-0CB2-4E81-971B-BADC60CDE3A5}"
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.addin b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.addin
index 502c9a8a07..9cbc94b106 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.addin
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.addin
@@ -73,6 +73,10 @@
 					  class="ICSharpCode.SharpDevelop.Project.Commands.StopDebuggingCommand"
 					  label="${res:XML.MainMenu.DebugMenu.Stop}"/>
 				</Condition>
+				<MenuItem id="SendToRubyConsoleSeparator" type="Separator"/>
+				<MenuItem id="SendLineToRubyConsole"
+					class="ICSharpCode.RubyBinding.SendLineToRubyConsoleCommand"
+					label="${res:ICSharpCode.RubyBinding.SendLineToRubyConsole}"/>
 			</MenuItem>
 		</Condition>
 	</Path>
@@ -165,25 +169,35 @@
 		<Pad id="RubyConsole"
 			category="Tools"
 			title="Ruby Console"
-			insertafter="Bookmarks"
 			insertbefore="DefinitionView"
 			icon="PadIcons.Output"
-			defaultPosition = "Bottom, Hidden"
+			defaultPosition="Bottom, Hidden"
 			class="ICSharpCode.RubyBinding.RubyConsolePad"/>
 	</Path>
 	
 	<Path name = "/Workspace/Icons">
 		<Icon id="RubyFileIcon"
 			extensions=".rb"
-			resource="Ruby.ProjectBrowser.File" />
+			resource="Ruby.ProjectBrowser.File"/>
 		<Icon id="RubyProjectIcon"
 			language="Ruby"
-			resource="Ruby.ProjectBrowser.Project" />
+			resource="Ruby.ProjectBrowser.Project"/>
 	</Path>
 
 	<Path name="/SharpDevelop/UnitTesting/TestFrameworks">
 		<TestFramework id="rbunit"
 			class="ICSharpCode.RubyBinding.RubyTestFramework"
-			supportedProjects=".rbproj" />
+			supportedProjects=".rbproj"/>
 	</Path>
+	
+	<Path name="/SharpDevelop/ViewContent/TextEditor/ContextMenu">
+		<Condition name="ActiveContentExtension" activeextension=".rb">
+			<MenuItem id="SendToRubyConsoleSeparator"
+			    insertafter="Indent"
+				type="Separator"/>
+			<MenuItem id="SendLineToRubyConsole"
+				class="ICSharpCode.RubyBinding.SendLineToRubyConsoleCommand"
+				label="${res:ICSharpCode.RubyBinding.SendLineToRubyConsole}"/>
+		</Condition>
+  </Path>
 </AddIn>
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.csproj b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.csproj
index 7eecd55616..61e284a029 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.csproj
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/RubyBinding.csproj
@@ -91,7 +91,11 @@
     <Compile Include="Src\ControlDispatcher.cs" />
     <Compile Include="Src\CreateTextWriterInfo.cs" />
     <Compile Include="Src\IControlDispatcher.cs" />
+    <Compile Include="Src\ILock.cs" />
+    <Compile Include="Src\IRubyConsole.cs" />
+    <Compile Include="Src\IRubyConsolePad.cs" />
     <Compile Include="Src\IRubyFileService.cs" />
+    <Compile Include="Src\IRubyWorkbench.cs" />
     <Compile Include="Src\RubyConsoleApplication.cs" />
     <Compile Include="Src\RubyConsoleCompletionData.cs" />
     <Compile Include="Src\ConvertProjectToRubyProjectCommand.cs" />
@@ -138,9 +142,13 @@
     <Compile Include="Src\RubyTestRunnerContext.cs" />
     <Compile Include="Src\RubyTestRunnerResponseFile.cs" />
     <Compile Include="Src\RubyTextEditorViewContent.cs" />
+    <Compile Include="Src\RubyWorkbench.cs" />
     <Compile Include="Src\RunDebugRubyCommand.cs" />
     <Compile Include="Src\RunRubyCommand.cs" />
     <Compile Include="Src\RubyConsoleTextEditor.cs" />
+    <Compile Include="Src\SendLineToRubyConsoleCommand.cs" />
+    <Compile Include="Src\StringListLock.cs" />
+    <Compile Include="Src\ThreadSafeRubyConsoleTextEditor.cs" />
     <EmbeddedResource Include="Resources\Ruby.xshd" />
     <EmbeddedResource Include="Resources\RubyOptionsPanel.xfrm" />
     <None Include="..\..\IronRuby\bin\Chiron.exe.config">
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/AddInOptions.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/AddInOptions.cs
deleted file mode 100644
index cd6487d153..0000000000
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/AddInOptions.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-// <file>
-//     <copyright see="prj:///doc/copyright.txt"/>
-//     <license see="prj:///doc/license.txt"/>
-//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
-//     <version>$Revision$</version>
-// </file>
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using ICSharpCode.Core;
-
-namespace ICSharpCode.RubyBinding
-{
-	/// <summary>
-	/// Holds the options for the RubyBinding AddIn
-	/// </summary>
-	public class AddInOptions
-	{
-		/// <summary>
-		/// The name of the options as read from the PropertyService.
-		/// </summary>
-		public static readonly string AddInOptionsName = "RubyBinding.Options";
-		
-		/// <summary>
-		/// The default Ruby console filename.
-		/// </summary>
-		public static readonly string DefaultRubyFileName = "ir.exe";
-		
-		#region Property names
-		public static readonly string RubyFileNameProperty = "RubyFileName";
-		#endregion
-		
-		Properties properties;
-		
-		public AddInOptions()
-			: this(PropertyService.Get(AddInOptionsName, new Properties()))
-		{
-		}
-		
-		/// <summary>
-		/// Creates the addin options class which will use
-		/// the options from the properties class specified.
-		/// </summary>
-		public AddInOptions(Properties properties)
-		{
-			this.properties = properties;
-		}
-		
-		/// <summary>
-		/// Gets or sets the Ruby console filename.
-		/// </summary>
-		public string RubyFileName {
-			get {
-				return properties.Get<string>(RubyFileNameProperty, GetDefaultRubyFileName());
-			}
-			set {
-				if (String.IsNullOrEmpty(value)) {
-					properties.Set(RubyFileNameProperty, GetDefaultRubyFileName());
-				} else {
-					properties.Set(RubyFileNameProperty, value);
-				}
-			}
-		}
-		
-		/// <summary>
-		/// Gets the path to the specified addin.
-		/// </summary>
-		/// <param name="addIn">The addin name: "${addin:ICSharpCode.RubyBinding}"</param>
-		protected virtual string GetAddInPath(string addIn)
-		{
-			return StringParser.Parse(addIn);
-		}
-		
-		/// <summary>
-		/// Returns the full path to ir.exe which is installed in the
-		/// Ruby addin folder.
-		/// </summary>
-		string GetDefaultRubyFileName()
-		{
-			string path = GetAddInPath("${addinpath:ICSharpCode.RubyBinding}");
-			return Path.Combine(path, DefaultRubyFileName);
-		}
-	}
-}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ConvertToRubyMenuCommand.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ConvertToRubyMenuCommand.cs
index 5e6950f434..e270bd89d1 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ConvertToRubyMenuCommand.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ConvertToRubyMenuCommand.cs
@@ -22,10 +22,10 @@ namespace ICSharpCode.RubyBinding
 		
 		public override void Run()
 		{
-			Run(WorkbenchSingleton.Workbench);
+			Run(new RubyWorkbench());
 		}
 		
-		protected void Run(IWorkbench workbench)
+		protected void Run(IRubyWorkbench workbench)
 		{
 			view = new RubyTextEditorViewContent(workbench);
 			string code = GenerateRubyCode();
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IConsoleTextEditor.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IConsoleTextEditor.cs
index 7bd82bba22..6b4582e3bc 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IConsoleTextEditor.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IConsoleTextEditor.cs
@@ -14,7 +14,7 @@ namespace ICSharpCode.RubyBinding
 	/// all the methods will be called on another thread not the main UI thread and will therefore need to
 	/// be invoked.
 	/// </summary>
-	public interface IConsoleTextEditor
+	public interface IConsoleTextEditor : IDisposable
 	{
 		/// <summary>
 		/// Fired when a key is pressed but before any text has been added to the text editor.
@@ -53,7 +53,7 @@ namespace ICSharpCode.RubyBinding
 		/// <summary>
 		/// Gets the current line the cursor is on. This is zero based.
 		/// </summary>
-		int Line {get;}
+		int Line {get; set;}
 		
 		/// <summary>
 		/// Gets the total number of lines in the text editor.
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ILock.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ILock.cs
new file mode 100644
index 0000000000..ba88f63542
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ILock.cs
@@ -0,0 +1,16 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.Collections.Generic;
+
+namespace ICSharpCode.RubyBinding
+{
+	public interface ILock : IDisposable
+	{
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsole.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsole.cs
new file mode 100644
index 0000000000..863d73bc08
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsole.cs
@@ -0,0 +1,16 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+
+namespace ICSharpCode.RubyBinding
+{
+	public interface IRubyConsole
+	{
+		void SendLine(string text);
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsolePad.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsolePad.cs
new file mode 100644
index 0000000000..bb13917aa2
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyConsolePad.cs
@@ -0,0 +1,18 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+
+namespace ICSharpCode.RubyBinding
+{
+	public interface IRubyConsolePad
+	{
+		void BringToFront();
+		IConsoleTextEditor ConsoleTextEditor { get; }
+		IRubyConsole RubyConsole { get; }
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyWorkbench.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyWorkbench.cs
new file mode 100644
index 0000000000..c20bcde3e5
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/IRubyWorkbench.cs
@@ -0,0 +1,18 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.SharpDevelop.Gui;
+
+namespace ICSharpCode.RubyBinding
+{
+	public interface IRubyWorkbench
+	{
+		IRubyConsolePad GetRubyConsolePad();
+		IViewContent ActiveViewContent { get; }
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsole.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsole.cs
index 132ac3893f..24cfbffe48 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsole.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsole.cs
@@ -16,27 +16,30 @@ using Microsoft.Scripting.Hosting.Shell;
 
 namespace ICSharpCode.RubyBinding
 {
-	public class RubyConsole : IConsole, IDisposable, IMemberProvider
+	public class RubyConsole : IConsole, IDisposable, IMemberProvider, IRubyConsole
 	{
+		IConsoleTextEditor textEditor;
 		int lineReceivedEventIndex = 0; // The index into the waitHandles array where the lineReceivedEvent is stored.
 		ManualResetEvent lineReceivedEvent = new ManualResetEvent(false);
 		ManualResetEvent disposedEvent = new ManualResetEvent(false);
 		WaitHandle[] waitHandles;
 		int promptLength;
-		IConsoleTextEditor textEditor;
-		List<string> previousLines = new List<string>();
+		bool firstPromptDisplayed;
+		string savedSendLineText;
 		CommandLineHistory commandLineHistory = new CommandLineHistory();
-		CommandLine commandLine;
 
-		public RubyConsole(IConsoleTextEditor textEditor, CommandLine commandLine)
+		protected List<string> unreadLines = new List<string>();
+		
+		public RubyConsole(IConsoleTextEditor textEditor)
 		{
 			waitHandles = new WaitHandle[] {lineReceivedEvent, disposedEvent};
 			
-			this.commandLine = commandLine;
 			this.textEditor = textEditor;
 			textEditor.PreviewKeyDown += ProcessPreviewKeyDown;
 		}
 		
+		public CommandLine CommandLine { get; set; }
+		
 		public void Dispose()
 		{
 			disposedEvent.Set();
@@ -64,9 +67,8 @@ namespace ICSharpCode.RubyBinding
 		
 		public string ReadLine(int autoIndentSize)
 		{
-			string indent = String.Empty;
+			string indent = GetIndent(autoIndentSize);
 			if (autoIndentSize > 0) {
-				indent = String.Empty.PadLeft(autoIndentSize);
 				Write(indent, Style.Prompt);
 			}
 			
@@ -77,16 +79,31 @@ namespace ICSharpCode.RubyBinding
 			return null;
 		}
 		
+		string GetIndent(int autoIndentSize)
+		{
+			return String.Empty.PadLeft(autoIndentSize);
+		}
+		
 		public void Write(string text, Style style)
 		{
 			textEditor.Write(text);
 
 			if (style == Style.Prompt) {
+				WriteSavedLineTextAfterFirstPrompt(text);
 				promptLength = text.Length;
 				textEditor.MakeCurrentContentReadOnly();
 			}
 		}
 		
+		void WriteSavedLineTextAfterFirstPrompt(string promptText)
+		{
+			firstPromptDisplayed = true;
+			if (savedSendLineText != null) {
+				textEditor.Write(savedSendLineText + "\r\n");
+				savedSendLineText = null;
+			}
+		}
+		
 		public void WriteLine(string text, Style style)
 		{
 			Write(text + Environment.NewLine, style);
@@ -105,32 +122,23 @@ namespace ICSharpCode.RubyBinding
 		/// </summary>
 		public bool IsLineAvailable {
 			get { 
-				lock (previousLines) {
-					return (previousLines.Count > 0);
+				lock (unreadLines) {
+					return (unreadLines.Count > 0);
 				}
 			}
 		}
 		
-		/// <summary>
-		/// Gets the lines that have not been returned by the ReadLine method. This does not
-		/// include the current line.
-		/// </summary>
-		public string[] GetUnreadLines()
-		{
-			return previousLines.ToArray();
-		}
-		
 		/// <summary>
 		/// Gets the member names of the specified item.
 		/// </summary>
 		public IList<string> GetMemberNames(string name)
 		{
-			return commandLine.GetMemberNames(name);
+			return CommandLine.GetMemberNames(name);
 		}
 		
 		public IList<string> GetGlobals(string name)
 		{
-			return commandLine.GetGlobals(name);
+			return CommandLine.GetGlobals(name);
 		}
 		
 		/// <summary>
@@ -185,13 +193,13 @@ namespace ICSharpCode.RubyBinding
 		}
 		
 		string ReadLineFromTextEditor()
-		{			
+		{
 			int result = WaitHandle.WaitAny(waitHandles);
 			if (result == lineReceivedEventIndex) {
-				lock (previousLines) {
-					string line = previousLines[0];
-					previousLines.RemoveAt(0);
-					if (previousLines.Count == 0) {
+				lock (unreadLines) {
+					string line = unreadLines[0];
+					unreadLines.RemoveAt(0);
+					if (unreadLines.Count == 0) {
 						lineReceivedEvent.Reset();
 					}
 					return line;
@@ -205,18 +213,26 @@ namespace ICSharpCode.RubyBinding
 		/// </summary>
 		void OnEnterKeyPressed()
 		{
-			lock (previousLines) {
-				// Move cursor to the end of the line.
-				textEditor.Column = GetLastTextEditorLine().Length;
-
-				// Append line.
-				string currentLine = GetCurrentLine();
-				previousLines.Add(currentLine);
-				commandLineHistory.Add(currentLine);
+			lock (unreadLines) {
+				MoveCursorToEndOfLastTextEditorLine();
+				SaveLastTextEditorLine();
 				
 				lineReceivedEvent.Set();
 			}
-		}			
+		}	
+		
+		void MoveCursorToEndOfLastTextEditorLine()
+		{
+			textEditor.Line = textEditor.TotalLines - 1;
+			textEditor.Column = GetLastTextEditorLine().Length;
+		}
+		
+		void SaveLastTextEditorLine()
+		{
+			string currentLine = GetCurrentLine();
+			unreadLines.Add(currentLine);
+			commandLineHistory.Add(currentLine);
+		}		
 	
 		string GetLastTextEditorLine()
 		{
@@ -300,5 +316,34 @@ namespace ICSharpCode.RubyBinding
 			RubyConsoleCompletionDataProvider completionProvider = new RubyConsoleCompletionDataProvider(this);
 			textEditor.ShowCompletionWindow(completionProvider);
 		}
+		
+		public void SendLine(string text)
+		{
+			using (ILock linesLock = CreateLock(unreadLines)) {
+				unreadLines.Add(text);					
+			}
+			FireLineReceivedEvent();
+			MoveCursorToEndOfLastTextEditorLine();
+			WriteLineIfFirstPromptHasBeenDisplayed(text);
+		}
+		
+		protected virtual ILock CreateLock(List<string> lines)
+		{
+			return new StringListLock(lines);
+		}
+		
+		protected virtual void FireLineReceivedEvent()
+		{
+			lineReceivedEvent.Set();
+		}
+		
+		void WriteLineIfFirstPromptHasBeenDisplayed(string text)
+		{
+			if (firstPromptDisplayed) {
+				WriteLine(text, Style.Out);
+			} else {
+				savedSendLineText = text;
+			}
+		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleHost.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleHost.cs
index 4df5b50516..556885887c 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleHost.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleHost.cs
@@ -25,6 +25,11 @@ namespace ICSharpCode.RubyBinding
 		public RubyConsoleHost(IConsoleTextEditor textEditor)
 		{
 			this.textEditor = textEditor;
+			rubyConsole = new RubyConsole(textEditor);
+		}
+		
+		public RubyConsole RubyConsole {
+			get { return rubyConsole; }
 		}
 		
 		/// <summary>
@@ -47,9 +52,6 @@ namespace ICSharpCode.RubyBinding
 			}			
 		}
 
-		/// <summary>
-		/// Runs the console.
-		/// </summary>
 		void RunConsole()
 		{
 			Run(new string[0]);
@@ -88,7 +90,7 @@ namespace ICSharpCode.RubyBinding
 		protected override IConsole CreateConsole(ScriptEngine engine, CommandLine commandLine, ConsoleOptions options)
 		{
 			SetOutput(new RubyOutputStream(textEditor));
-			rubyConsole = new RubyConsole(textEditor, commandLine);
+			rubyConsole.CommandLine = commandLine;
 			return rubyConsole;
 		}
 	}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsolePad.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsolePad.cs
index 7a0bfb1095..acf56afba7 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsolePad.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsolePad.cs
@@ -11,23 +11,31 @@ using ICSharpCode.SharpDevelop.Gui;
 
 namespace ICSharpCode.RubyBinding
 {
-	public class RubyConsolePad : AbstractPadContent
+	public class RubyConsolePad : AbstractPadContent, IRubyConsolePad
 	{
-		RubyConsoleTextEditor consoleTextEditor;
+		ThreadSafeRubyConsoleTextEditor consoleTextEditor;
 		AvalonEdit.TextEditor textEditor;
 		RubyConsoleHost host;
 		
 		public RubyConsolePad()
 		{
 			textEditor = new AvalonEdit.TextEditor();
-			consoleTextEditor = new RubyConsoleTextEditor(textEditor);
+			consoleTextEditor = new ThreadSafeRubyConsoleTextEditor(textEditor);
 			host = new RubyConsoleHost(consoleTextEditor);
-			host.Run();	
+			host.Run();
 		}
 		
+		public IConsoleTextEditor ConsoleTextEditor {
+			get { return consoleTextEditor; }
+		}
+		
+		public IRubyConsole RubyConsole {
+			get { return host.RubyConsole; }
+ 		}
+ 		
 		public override object Control {
 			get { return textEditor; }
-		}		
+		}
 		
 		public override void Dispose()
 		{
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleTextEditor.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleTextEditor.cs
index 2553c7d026..250f68e72d 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleTextEditor.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyConsoleTextEditor.cs
@@ -19,23 +19,14 @@ namespace ICSharpCode.RubyBinding
 {
 	public class RubyConsoleTextEditor : IConsoleTextEditor
 	{
-		delegate string GetLineInvoker(int index);
-
 		TextEditor textEditor;
 		Color customLineColour = Color.LightGray;
 		BeginReadOnlySectionProvider readOnlyRegion;
-		IControlDispatcher dispatcher;
 		CompletionWindow completionWindow;
 		
 		public RubyConsoleTextEditor(TextEditor textEditor)
-			: this(textEditor, new ControlDispatcher(textEditor))
-		{
-		}
-		
-		public RubyConsoleTextEditor(TextEditor textEditor, IControlDispatcher dispatcher)
 		{
 			this.textEditor = textEditor;
-			this.dispatcher = dispatcher;
 			readOnlyRegion = new BeginReadOnlySectionProvider();
 			textEditor.TextArea.ReadOnlySectionProvider = readOnlyRegion;
 			textEditor.PreviewKeyDown += OnTextEditorPreviewKeyDown;
@@ -49,6 +40,11 @@ namespace ICSharpCode.RubyBinding
 		}
 		
 		public event ConsoleTextEditorKeyEventHandler PreviewKeyDown;
+	
+		public void Dispose()
+		{
+			textEditor.PreviewKeyDown -= OnTextEditorPreviewKeyDown;
+		}
 		
 		public Color CustomLineColour {
 			get { return customLineColour; }
@@ -56,14 +52,9 @@ namespace ICSharpCode.RubyBinding
 		
 		public void Write(string text)
 		{
-			if (dispatcher.CheckAccess()) {
-				TextLocation location = GetCurrentCursorLocation();
-				int offset = textEditor.Document.GetOffset(location);
-				textEditor.Document.Insert(offset, text);
-			} else {
-				Action<string> action = Write;
-				dispatcher.Invoke(action, new object[] {text});
-			}
+			TextLocation location = GetCurrentCursorLocation();
+			int offset = textEditor.Document.GetOffset(location);
+			textEditor.Document.Insert(offset, text);
 		}
 		
 		TextLocation GetCurrentCursorLocation()
@@ -84,11 +75,9 @@ namespace ICSharpCode.RubyBinding
 			get { return textEditor.SelectionLength; }
 		}
 
-		/// <summary>
-		/// Gets the current cursor line.
-		/// </summary>
 		public int Line {
 			get { return textEditor.TextArea.Caret.Line - 1; }
+			set { textEditor.TextArea.Caret.Line = value + 1; }
 		}
 
 		/// <summary>
@@ -103,13 +92,8 @@ namespace ICSharpCode.RubyBinding
 		/// </summary>
 		public string GetLine(int index)
 		{
-			if (dispatcher.CheckAccess()) {
-				DocumentLine line = textEditor.Document.GetLineByNumber(index + 1);
-				return textEditor.Document.GetText(line);
-			} else {
-				GetLineInvoker invoker = new GetLineInvoker(GetLine);
-				return (string)dispatcher.Invoke(invoker, new object[] {index});
-			}
+			DocumentLine line = textEditor.Document.GetLineByNumber(index + 1);
+			return textEditor.Document.GetText(line);
 		}
 		
 		/// <summary>
@@ -117,14 +101,9 @@ namespace ICSharpCode.RubyBinding
 		/// </summary>
 		public void Replace(int index, int length, string text)
 		{
-			if (dispatcher.CheckAccess()) {
-				DocumentLine line = textEditor.Document.GetLineByNumber(textEditor.TextArea.Caret.Line);
-				int offset = line.Offset + index;
-				textEditor.Document.Replace(offset, length, text);
-			} else {
-				Action<int, int, string> action = Replace;
-				dispatcher.Invoke(action, new object[] {index, length, text});
-			}
+			DocumentLine line = textEditor.Document.GetLineByNumber(textEditor.TextArea.Caret.Line);
+			int offset = line.Offset + index;
+			textEditor.Document.Replace(offset, length, text);
 		}
 	
 		/// <summary>
@@ -132,12 +111,7 @@ namespace ICSharpCode.RubyBinding
 		/// </summary>
 		public void MakeCurrentContentReadOnly()
 		{
-			if (dispatcher.CheckAccess()) {
-				readOnlyRegion.EndOffset = textEditor.Document.TextLength;
-			} else {
-				Action action = MakeCurrentContentReadOnly;
-				dispatcher.Invoke(action);
-			}
+			readOnlyRegion.EndOffset = textEditor.Document.TextLength;
 		}
 		
 		public void ShowCompletionWindow(RubyConsoleCompletionDataProvider completionDataProvider)
@@ -157,8 +131,6 @@ namespace ICSharpCode.RubyBinding
 			}
 			completionWindow.ExpectInsertionBeforeStart = true;
 			completionWindow.Show();
-			Action<CompletionWindow> action = ShowCompletionWindow;
-			completionWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, action, completionWindow);
 		}
 		
 		void ShowCompletionWindow(CompletionWindow window)
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyTextEditorViewContent.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyTextEditorViewContent.cs
index b31a875eb9..a361ec74b5 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyTextEditorViewContent.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyTextEditorViewContent.cs
@@ -20,9 +20,9 @@ namespace ICSharpCode.RubyBinding
 		ITextEditor textEditor;
 		ITextEditorOptions textEditorOptions;
 		
-		public RubyTextEditorViewContent(IWorkbench workbench)
+		public RubyTextEditorViewContent(IRubyWorkbench workbench)
 		{
-			Init(workbench.ActiveWorkbenchWindow.ActiveViewContent);
+			Init(workbench.ActiveViewContent);
 		}
 		
 		public RubyTextEditorViewContent(IViewContent view)
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyWorkbench.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyWorkbench.cs
new file mode 100644
index 0000000000..38a84b2e76
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyWorkbench.cs
@@ -0,0 +1,33 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Gui;
+
+namespace ICSharpCode.RubyBinding
+{
+	public class RubyWorkbench : IRubyWorkbench
+	{
+		IWorkbench workbench;
+		
+		public RubyWorkbench()
+		{
+			workbench = WorkbenchSingleton.Workbench;
+		}		
+		
+		public IViewContent ActiveViewContent {
+			get { return workbench.ActiveViewContent; }
+		}
+		
+		public IRubyConsolePad GetRubyConsolePad()
+		{
+			PadDescriptor padDescriptor = workbench.GetPad(typeof(RubyConsolePad));
+			return padDescriptor.PadContent as IRubyConsolePad;
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunDebugRubyCommand.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunDebugRubyCommand.cs
index 6d58226561..ebe7548873 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunDebugRubyCommand.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunDebugRubyCommand.cs
@@ -15,14 +15,14 @@ namespace ICSharpCode.RubyBinding
 {
 	public class RunDebugRubyCommand : RunRubyCommand
 	{
-		public RunDebugRubyCommand(IWorkbench workbench, RubyAddInOptions options, IDebugger debugger) 
+		public RunDebugRubyCommand(IRubyWorkbench workbench, RubyAddInOptions options, IDebugger debugger) 
 			: base(workbench, options, debugger)
 		{
 			Debug = true;
 		}
 		
 		public RunDebugRubyCommand()
-			: this(WorkbenchSingleton.Workbench, new RubyAddInOptions(), DebuggerService.CurrentDebugger)
+			: this(new RubyWorkbench(), new RubyAddInOptions(), DebuggerService.CurrentDebugger)
 		{
 		}
 	}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunRubyCommand.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunRubyCommand.cs
index f14a27b006..e407b712b4 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunRubyCommand.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RunRubyCommand.cs
@@ -23,15 +23,15 @@ namespace ICSharpCode.RubyBinding
 	{
 		IDebugger debugger;
 		RubyAddInOptions options;
-		IWorkbench workbench;
+		IRubyWorkbench workbench;
 		bool debug;
 		
 		public RunRubyCommand()
-			: this(WorkbenchSingleton.Workbench, new RubyAddInOptions(), DebuggerService.CurrentDebugger)
+			: this(new RubyWorkbench(), new RubyAddInOptions(), DebuggerService.CurrentDebugger)
 		{
 		}
 		
-		public RunRubyCommand(IWorkbench workbench, RubyAddInOptions options, IDebugger debugger)
+		public RunRubyCommand(IRubyWorkbench workbench, RubyAddInOptions options, IDebugger debugger)
 		{
 			this.workbench = workbench;
 			this.debugger = debugger;
@@ -68,7 +68,7 @@ namespace ICSharpCode.RubyBinding
 		}
 		
 		FileName WorkbenchPrimaryFileName {
-			get { return workbench.ActiveWorkbenchWindow.ActiveViewContent.PrimaryFileName; }
+			get { return workbench.ActiveViewContent.PrimaryFileName; }
 		}
 		
 		string GetArguments()
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/SendLineToRubyConsoleCommand.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/SendLineToRubyConsoleCommand.cs
new file mode 100644
index 0000000000..ccc7041679
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/SendLineToRubyConsoleCommand.cs
@@ -0,0 +1,73 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.Core;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Gui;
+
+namespace ICSharpCode.RubyBinding
+{
+	public class SendLineToRubyConsoleCommand : AbstractCommand
+	{
+		IRubyWorkbench workbench;
+		IRubyConsolePad consolePad;
+		RubyTextEditorViewContent textEditorView;
+		ITextEditor activeTextEditor;
+		IRubyConsole RubyConsole;
+		string lineFromActiveTextEditor;
+		
+		public SendLineToRubyConsoleCommand()
+			: this(new RubyWorkbench())
+		{
+		}
+		
+		public SendLineToRubyConsoleCommand(IRubyWorkbench workbench)
+		{
+			this.workbench = workbench;
+			
+			textEditorView = new RubyTextEditorViewContent(workbench);
+			activeTextEditor = textEditorView.TextEditor;
+		}	
+		
+		public override void Run()
+		{
+			GetLineFromActiveTextEditor();
+			GetRubyConsolePad();
+			ShowRubyConsolePad();
+			AppendLineToRubyConsole();
+		}
+		
+		void GetLineFromActiveTextEditor()
+		{
+			int lineNumber = activeTextEditor.Caret.Line;
+			IDocumentLine documentLine =  activeTextEditor.Document.GetLine(lineNumber);
+			lineFromActiveTextEditor = documentLine.Text;
+		}
+		
+		void GetRubyConsolePad()
+		{
+			consolePad = workbench.GetRubyConsolePad();
+		}
+				
+		void ShowRubyConsolePad()
+		{
+			consolePad.BringToFront();
+		}
+		
+		void AppendLineToRubyConsole()
+		{
+			GetRubyConsole();
+			RubyConsole.SendLine(lineFromActiveTextEditor);
+		}
+		
+		void GetRubyConsole()
+		{
+			RubyConsole = consolePad.RubyConsole;
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/StringListLock.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/StringListLock.cs
new file mode 100644
index 0000000000..3fa5a74bdc
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/StringListLock.cs
@@ -0,0 +1,39 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace ICSharpCode.RubyBinding
+{
+	public class StringListLock : ILock
+	{
+		List<string> lines;
+		
+		public StringListLock(List<string> lines)
+		{
+			this.lines = lines;
+			Lock();
+		}
+		
+		void Lock()
+		{
+			Monitor.Enter(lines);
+		}
+		
+		public void Dispose()
+		{
+			Unlock();
+		}
+		
+		void Unlock()
+		{
+			Monitor.Exit(lines);
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ThreadSafeRubyConsoleTextEditor.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ThreadSafeRubyConsoleTextEditor.cs
new file mode 100644
index 0000000000..f313949709
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/ThreadSafeRubyConsoleTextEditor.cs
@@ -0,0 +1,117 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.AvalonEdit;
+
+namespace ICSharpCode.RubyBinding
+{
+	public class ThreadSafeRubyConsoleTextEditor : IConsoleTextEditor
+	{
+		delegate string GetLineInvoker(int index);
+		
+		IConsoleTextEditor consoleTextEditor;
+		IControlDispatcher dispatcher;
+		
+		public ThreadSafeRubyConsoleTextEditor(TextEditor textEditor)
+			: this(new RubyConsoleTextEditor(textEditor), new ControlDispatcher(textEditor))
+		{
+		}
+		
+		public ThreadSafeRubyConsoleTextEditor(IConsoleTextEditor consoleTextEditor, IControlDispatcher dispatcher)
+		{
+			this.consoleTextEditor = consoleTextEditor;
+			this.dispatcher = dispatcher;
+		}
+		
+		public event ConsoleTextEditorKeyEventHandler PreviewKeyDown {
+			add { consoleTextEditor.PreviewKeyDown += value; }
+			remove { consoleTextEditor.PreviewKeyDown -= value; }
+		}
+		
+		public void Dispose()
+		{
+			consoleTextEditor.Dispose();
+		}
+		
+		public int Column {
+			get { return consoleTextEditor.Column; }
+			set { consoleTextEditor.Column = value; }
+		}
+		
+		public int SelectionLength {
+			get { return consoleTextEditor.SelectionLength; }
+		}
+		
+		public int SelectionStart {
+			get { return consoleTextEditor.SelectionStart; }
+		}
+		
+		public int Line {
+			get { return consoleTextEditor.Line; }
+			set { consoleTextEditor.Line = value; }
+		}
+		
+		public int TotalLines {
+			get { return consoleTextEditor.TotalLines; }
+		}
+		
+		public bool IsCompletionWindowDisplayed {
+			get { return consoleTextEditor.IsCompletionWindowDisplayed; }
+		}
+		
+		public void Write(string text)
+		{			
+			if (dispatcher.CheckAccess()) {
+				consoleTextEditor.Write(text);
+			} else {
+				Action<string> action = Write;
+				dispatcher.Invoke(action, text);
+			}
+		}
+		
+		public void Replace(int index, int length, string text)
+		{
+			if (dispatcher.CheckAccess()) {
+				consoleTextEditor.Replace(index, length, text);
+			} else {
+				Action<int, int, string> action = Replace;
+				dispatcher.Invoke(action, index, length, text);
+			}
+		}
+		
+		public string GetLine(int index)
+		{
+			if (dispatcher.CheckAccess()) {
+				return consoleTextEditor.GetLine(index);
+			} else {
+				GetLineInvoker invoker = new GetLineInvoker(GetLine);
+				return (string)dispatcher.Invoke(invoker, index);
+			}
+		}
+		
+		public void ShowCompletionWindow(RubyConsoleCompletionDataProvider completionDataProvider)
+		{
+			if (dispatcher.CheckAccess()) {
+				consoleTextEditor.ShowCompletionWindow(completionDataProvider);
+			} else {
+				Action<RubyConsoleCompletionDataProvider> action = ShowCompletionWindow;
+				dispatcher.Invoke(action, completionDataProvider);
+			}
+		}
+		
+		public void MakeCurrentContentReadOnly()
+		{
+			if (dispatcher.CheckAccess()) {
+				consoleTextEditor.MakeCurrentContentReadOnly();
+			} else {
+				Action action = MakeCurrentContentReadOnly;
+				dispatcher.Invoke(action);
+			}
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/DisposedRubyConsoleTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/DisposedRubyConsoleTestFixture.cs
index 9bb244b2c3..8d7756c98f 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/DisposedRubyConsoleTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/DisposedRubyConsoleTestFixture.cs
@@ -16,21 +16,21 @@ namespace RubyBinding.Tests.Console
 	/// Tests the disposing of the RubyConsole.
 	/// </summary>
 	[TestFixture]
-	public class DisposedRubyConsoleTestFixture
+	public class DisposedRubyConsoleTestFixture : RubyConsoleTestsBase
 	{
 		[Test]
 		public void RubyConsoleImplementsIDisposable()
 		{
-			RubyConsole console = new RubyConsole(new MockConsoleTextEditor(), null);
-			Assert.IsNotNull(console as IDisposable);
+			base.CreateRubyConsole();
+			Assert.IsNotNull(TestableRubyConsole as IDisposable);
 		}
 		
 		[Test]
 		public void ReadLineReturnsNullWhenConsoleDisposed()
 		{
-			RubyConsole console = new RubyConsole(new MockConsoleTextEditor(), null);
-			console.Dispose();
-			Assert.IsNull(console.ReadLine(0));
+			base.CreateRubyConsole();
+			TestableRubyConsole.Dispose();
+			Assert.IsNull(TestableRubyConsole.ReadLine(0));
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/KeysPressedWhenCompletionWindowOpenTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/KeysPressedWhenCompletionWindowOpenTestFixture.cs
index 4623b1227b..07425f8722 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/KeysPressedWhenCompletionWindowOpenTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/KeysPressedWhenCompletionWindowOpenTestFixture.cs
@@ -21,42 +21,39 @@ namespace RubyBinding.Tests.Console
 	/// that the up, down, home and end key do not close this window.
 	/// </summary>
 	[TestFixture]
-	public class KeysPressedWhenCompletionWindowOpenTestFixture
+	public class KeysPressedWhenCompletionWindowOpenTestFixture : RubyConsoleTestsBase
 	{
-		RubyConsole rubyConsole;
-		MockConsoleTextEditor textEditor;
 		string prompt = ">>> ";
 		
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			rubyConsole = new RubyConsole(textEditor, null);
-			rubyConsole.Write(prompt, Style.Prompt);
+			base.CreateRubyConsole();
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 			
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
-			rubyConsole.Write(prompt, Style.Prompt);
-			textEditor.RaisePreviewKeyDownEvent(Key.B);
-			textEditor.RaisePreviewKeyDownEvent(Key.OemPeriod);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.B);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.OemPeriod);
 		}
 		
 		[Test]
 		public void ShowCompletionWindowIsCalled()
 		{
-			Assert.IsTrue(textEditor.IsShowCompletionWindowCalled);
+			Assert.IsTrue(MockConsoleTextEditor.IsShowCompletionWindowCalled);
 		}
 		
 		[Test]
 		public void UpKeyDoesNothing()
 		{
-			Assert.IsFalse(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up));
+			Assert.IsFalse(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up));
 		}
 		
 		[Test]
 		public void DownKeyDoesNothing()
 		{
-			Assert.IsFalse(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down));
+			Assert.IsFalse(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down));
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCodeCompletionTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCodeCompletionTestFixture.cs
index b5c7a4afe6..a4c72e35e0 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCodeCompletionTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCodeCompletionTestFixture.cs
@@ -20,29 +20,26 @@ namespace RubyBinding.Tests.Console
 	/// When the dot character is typed in after an object the code completion window should appear.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleCodeCompletionTestFixture
+	public class RubyConsoleCodeCompletionTestFixture : RubyConsoleTestsBase
 	{
-		MockConsoleTextEditor textEditor;
-		RubyConsole console;
 		string prompt = ">>> ";
 		bool showCompletionWindowCalledBeforeDotTypedIn;
 		
 		[TestFixtureSetUp]
 		public void SetUpFixture()
 		{
-			textEditor = new MockConsoleTextEditor();
-			console = new RubyConsole(textEditor, null);
-			console.WriteLine(prompt, Style.Prompt);
-									
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			showCompletionWindowCalledBeforeDotTypedIn = textEditor.IsShowCompletionWindowCalled;
-			textEditor.RaisePreviewKeyDownEvent(Key.OemPeriod);		
+			base.CreateRubyConsole();
+			TestableRubyConsole.WriteLine(prompt, Style.Prompt);
+			
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+ 			showCompletionWindowCalledBeforeDotTypedIn = MockConsoleTextEditor.IsShowCompletionWindowCalled;
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.OemPeriod);	
 		}
 		
 		[Test]
 		public void ShowCompletionWindowCalled()
 		{
-			Assert.IsTrue(textEditor.IsShowCompletionWindowCalled);
+			Assert.IsTrue(MockConsoleTextEditor.IsShowCompletionWindowCalled);
 		}
 
 		[Test]
@@ -54,7 +51,7 @@ namespace RubyBinding.Tests.Console
 		[Test]
 		public void RubyConsoleCompletionDataProviderPassedToShowCompletionWindowMethod()
 		{
-			Assert.IsInstanceOf(typeof(RubyConsoleCompletionDataProvider), textEditor.CompletionDataProvider);
+			Assert.IsInstanceOf(typeof(RubyConsoleCompletionDataProvider), MockConsoleTextEditor.CompletionProviderPassedToShowCompletionWindow);
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCommandLineHistoryTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCommandLineHistoryTestFixture.cs
index 45738efb16..defb05f5b6 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCommandLineHistoryTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCommandLineHistoryTestFixture.cs
@@ -21,74 +21,71 @@ namespace RubyBinding.Tests.Console
 	/// Tests the RubyConsole's command line history.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleCommandLineHistoryTestFixture
+	public class RubyConsoleCommandLineHistoryTestFixture : RubyConsoleTestsBase
 	{
-		RubyConsole rubyConsole;
-		MockConsoleTextEditor textEditor;
 		string prompt = ">>> ";
 		
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			rubyConsole = new RubyConsole(textEditor, null);
-			rubyConsole.Write(prompt, Style.Prompt);
+			base.CreateRubyConsole();
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 			
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
-			rubyConsole.Write(prompt, Style.Prompt);
-			textEditor.RaisePreviewKeyDownEvent(Key.B);
-			textEditor.RaisePreviewKeyDownEvent(Key.C);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
-			rubyConsole.Write(prompt, Style.Prompt);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.B);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.C);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 		}
 
 		[Test]
 		public void UpArrowKeyPressed()
 		{
-			Assert.IsTrue(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up));
+			Assert.IsTrue(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up));
 		}
 		
 		[Test]
 		public void CurrentLineAfterUpArrowKeyPressed()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
-			Assert.AreEqual("BC", rubyConsole.GetCurrentLine());
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
+			Assert.AreEqual("BC", TestableRubyConsole.GetCurrentLine());
 		}
 		
 		[Test]
 		public void TextEditorCursorIsAtEndOfLineAfterUpArrowKeyPressed()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
-			Assert.AreEqual(prompt.Length + 2, textEditor.Column);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
+			Assert.AreEqual(prompt.Length + 2, MockConsoleTextEditor.Column);
 		}
 		
 		[Test]
 		public void TextAfterUpArrowKeyPressedTwiceThenDownArrowKey()
 		{
 			UpArrowKeyPressedTwiceThenDownArrowKey();
-			Assert.AreEqual("BC", rubyConsole.GetCurrentLine());
+			Assert.AreEqual("BC", TestableRubyConsole.GetCurrentLine());
 		}
 
 		[Test]
 		public void TextEditorCursorAfterUpArrowKeyPressedTwice()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
-			Assert.AreEqual(prompt.Length + 1, textEditor.Column);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
+			Assert.AreEqual(prompt.Length + 1, MockConsoleTextEditor.Column);
 		}
 		
 		[Test]
 		public void DownArrowKeyHandled()
 		{
-			Assert.IsTrue(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down));
+			Assert.IsTrue(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down));
 		}
 		
 		void UpArrowKeyPressedTwiceThenDownArrowKey()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down);
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCurrentLineTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCurrentLineTestFixture.cs
index e8cc1ddca1..eda4033e08 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCurrentLineTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleCurrentLineTestFixture.cs
@@ -23,16 +23,16 @@ namespace RubyBinding.Tests.Console
 	[TestFixture]
 	public class RubyConsoleCurrentLineTestFixture
 	{
-		RubyConsole rubyConsole;
+		TestableRubyConsole rubyConsole;
 		MockConsoleTextEditor textEditor;
 		string prompt = ">>> ";
 		
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			rubyConsole = new RubyConsole(textEditor, null);
+			rubyConsole = new TestableRubyConsole();
 			rubyConsole.Write(prompt, Style.Prompt);
+			textEditor = rubyConsole.MockConsoleTextEditor;
 		}
 		
 		[Test]
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleEnterKeyTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleEnterKeyTestFixture.cs
index 9c5ee72b66..127d3d69ca 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleEnterKeyTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleEnterKeyTestFixture.cs
@@ -20,48 +20,45 @@ namespace RubyBinding.Tests.Console
 	/// will break the line and move the last part to the second line.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleEnterKeyTestFixture
+	public class RubyConsoleEnterKeyTestFixture : RubyConsoleTestsBase
 	{
-		MockConsoleTextEditor textEditor;
-		RubyConsole console;
 		string prompt = ">>> ";
 
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			console = new RubyConsole(textEditor, null);
-			console.Write(prompt, Style.Prompt);
+			base.CreateRubyConsole();
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 		}
 		
 		[Test]
 		public void EnterKeyDoesNotBreakUpExistingLine()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEvent(Key.B);
-			textEditor.SelectionStart = 1 + prompt.Length;
-			textEditor.Column = 1 + prompt.Length;
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.B);
+			MockConsoleTextEditor.SelectionStart = 1 + prompt.Length;
+			MockConsoleTextEditor.Column = 1 + prompt.Length;
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
 			
-			Assert.AreEqual(">>> AB\r\n", textEditor.Text);
+			Assert.AreEqual(">>> AB\r\n", MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void PreviousLineIsReadOnlyAfterEnterPressed()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEvent(Key.B);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
-			console.Write(prompt, Style.Prompt);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.B);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 			
 			// Move up a line with cursor.
-			textEditor.Line = 0;
-			textEditor.RaisePreviewKeyDownEvent(Key.C);
+			MockConsoleTextEditor.Line = 0;
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.C);
 			
 			string expectedText = 
 				">>> AB\r\n" +
 				">>> ";
-			Assert.AreEqual(expectedText, textEditor.Text);
+			Assert.AreEqual(expectedText, MockConsoleTextEditor.Text);
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleHomeKeyTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleHomeKeyTestFixture.cs
index b727536c85..a41b207d00 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleHomeKeyTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleHomeKeyTestFixture.cs
@@ -20,37 +20,34 @@ namespace RubyBinding.Tests.Console
 	/// The Home Key should return the user to the start of the line after the prompt.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleHomeKeyTestFixture
+	public class RubyConsoleHomeKeyTestFixture : RubyConsoleTestsBase
 	{
-		MockConsoleTextEditor textEditor;
-		RubyConsole console;
 		string prompt = ">>> ";
 
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			console = new RubyConsole(textEditor, null);
-			console.Write(prompt, Style.Prompt);
+			base.CreateRubyConsole();
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 		}
 		
 		[Test]
 		public void HomeKeyPressedWhenNoUserTextInConsole()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Home);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Home);
 		
 			int expectedColumn = prompt.Length;
-			Assert.AreEqual(expectedColumn, textEditor.Column);
+			Assert.AreEqual(expectedColumn, MockConsoleTextEditor.Column);
 		}
 		
 		[Test]
 		public void HomeKeyPressedWhenTextInConsole()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Home);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Home);
 
 			int expectedColumn = prompt.Length;
-			Assert.AreEqual(expectedColumn, textEditor.Column);
+			Assert.AreEqual(expectedColumn, MockConsoleTextEditor.Column);
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadOnlyRegionsTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadOnlyRegionsTestFixture.cs
index 86227ea7b7..0306b2337f 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadOnlyRegionsTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadOnlyRegionsTestFixture.cs
@@ -20,148 +20,145 @@ namespace RubyBinding.Tests.Console
 	/// RubyConsole itself restricts typing itself by handling key press events.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleReadOnlyRegionsTestFixture
+	public class RubyConsoleReadOnlyRegionsTestFixture : RubyConsoleTestsBase
 	{
-		RubyConsole console;
-		MockConsoleTextEditor textEditor;
 		string prompt = ">>> ";
 
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			console = new RubyConsole(textEditor, null);
-			console.Write(prompt, Style.Prompt);
+			base.CreateRubyConsole();
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 		}
 		
 		[Test]
 		public void MakeCurrentContentReadOnlyIsCalled()
 		{
-			Assert.IsTrue(textEditor.IsMakeCurrentContentReadOnlyCalled);
+			Assert.IsTrue(MockConsoleTextEditor.IsMakeCurrentContentReadOnlyCalled);
 		}
 
 		[Test]
 		public void LeftArrowThenInsertNewCharacterInsertsText()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEvent(Key.B);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
-			textEditor.RaisePreviewKeyDownEvent(Key.C);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.B);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.C);
 			
-			Assert.AreEqual("ACB", console.GetCurrentLine());
+			Assert.AreEqual("ACB", TestableRubyConsole.GetCurrentLine());
 		}
 		
 		[Test]
 		public void MoveOneCharacterIntoPromptTypingShouldBePrevented()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
 			
-			Assert.AreEqual(String.Empty, console.GetCurrentLine());
+			Assert.AreEqual(String.Empty, TestableRubyConsole.GetCurrentLine());
 		}
 
 		[Test]
 		public void MoveOneCharacterIntoPromptAndBackspaceKeyShouldNotRemoveAnything()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
 			
-			Assert.AreEqual("A", console.GetCurrentLine());
-			Assert.AreEqual(prompt + "A", textEditor.Text);
+			Assert.AreEqual("A", TestableRubyConsole.GetCurrentLine());
+			Assert.AreEqual(prompt + "A", MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void MoveTwoCharactersIntoPromptAndBackspaceKeyShouldNotRemoveAnything()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
 			
-			Assert.AreEqual("A", console.GetCurrentLine());
-			Assert.AreEqual(prompt + "A", textEditor.Text);
+			Assert.AreEqual("A", TestableRubyConsole.GetCurrentLine());
+			Assert.AreEqual(prompt + "A", MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void SelectLastCharacterOfPromptThenPressingTheBackspaceKeyShouldNotRemoveAnything()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.SelectionStart = prompt.Length - 1;
-			textEditor.SelectionLength = 2;
-			textEditor.Column += 2;
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.SelectionStart = prompt.Length - 1;
+			MockConsoleTextEditor.SelectionLength = 2;
+			MockConsoleTextEditor.Column += 2;
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
 			
-			Assert.AreEqual("A", console.GetCurrentLine());
-			Assert.AreEqual(prompt + "A", textEditor.Text);
+			Assert.AreEqual("A", TestableRubyConsole.GetCurrentLine());
+			Assert.AreEqual(prompt + "A", MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void CanMoveIntoPromptRegionWithLeftCursorKey()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
-			Assert.IsFalse(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left));
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left);
+			Assert.IsFalse(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Left));
 		}
 		
 		[Test]
 		public void CanMoveOutOfPromptRegionWithRightCursorKey()
 		{
-			textEditor.Column = 0;
-			Assert.IsFalse(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Right));
+			MockConsoleTextEditor.Column = 0;
+			Assert.IsFalse(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Right));
 		}
 		
 		[Test]
 		public void CanMoveOutOfPromptRegionWithUpCursorKey()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
-			console.Write(prompt, Style.Prompt);
-			textEditor.Column = 0;
-			Assert.IsFalse(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up));
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
+			MockConsoleTextEditor.Column = 0;
+			Assert.IsFalse(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Up));
 		}
 		
 		[Test]
 		public void CanMoveInReadOnlyRegionWithDownCursorKey()
 		{
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
-			console.Write(prompt, Style.Prompt);
-			textEditor.Column = 0;
-			textEditor.Line = 0;
-			Assert.IsFalse(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down));
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
+			MockConsoleTextEditor.Column = 0;
+			MockConsoleTextEditor.Line = 0;
+			Assert.IsFalse(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Down));
 		}		
 		
 		[Test]
 		public void BackspaceKeyPressedIgnoredIfLineIsEmpty()
 		{
-			Assert.IsTrue(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back));
+			Assert.IsTrue(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back));
 		}
 		
 		[Test]
 		public void BackspaceOnPreviousLine()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.RaisePreviewKeyDownEvent(Key.B);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.B);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
 
-			console.Write(prompt, Style.Prompt);
+			TestableRubyConsole.Write(prompt, Style.Prompt);
 			
-			textEditor.RaisePreviewKeyDownEvent(Key.C);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.C);
 			
 			// Move up a line with cursor.
-			textEditor.Line = 0;
+			MockConsoleTextEditor.Line = 0;
 			
-			Assert.IsTrue(textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back));
-			Assert.AreEqual("C", console.GetCurrentLine());
+			Assert.IsTrue(MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back));
+			Assert.AreEqual("C", TestableRubyConsole.GetCurrentLine());
 		}
 
 		[Test]
 		public void CanBackspaceFirstCharacterOnLine()
 		{
-			textEditor.RaisePreviewKeyDownEvent(Key.A);
-			textEditor.Column = 5;
-			textEditor.SelectionStart = 5;
-			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			MockConsoleTextEditor.Column = 5;
+			MockConsoleTextEditor.SelectionStart = 5;
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Back);
 			
-			Assert.AreEqual(String.Empty, console.GetCurrentLine());
+			Assert.AreEqual(String.Empty, TestableRubyConsole.GetCurrentLine());
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadTestFixture.cs
index efaa2b6cf2..d01a35a414 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleReadTestFixture.cs
@@ -19,22 +19,19 @@ namespace RubyBinding.Tests.Console
 	/// Tests the RubyConsole ReadLine method.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleReadTestFixture
+	public class RubyConsoleReadTestFixture : RubyConsoleTestsBase
 	{
-		RubyConsole rubyConsole;
 		int initialAutoIndentSize = 4;
 		string readLine;
 		int autoIndentSize;
-		MockConsoleTextEditor mockTextEditor;
 		bool raiseKeyPressEvent;
 		bool raiseDialogKeyPressEvent;
 		
 		[TestFixtureSetUp]
 		public void Init()
 		{
-			mockTextEditor = new MockConsoleTextEditor();
-			rubyConsole = new RubyConsole(mockTextEditor, null);
-
+			base.CreateRubyConsole();
+			
 			autoIndentSize = initialAutoIndentSize;
 			Thread thread = new Thread(ReadLineFromConsoleOnDifferentThread);
 			thread.Start();
@@ -42,16 +39,16 @@ namespace RubyBinding.Tests.Console
 			int sleepInterval = 10;
 			int maxWait = 2000;
 			int currentWait = 0;
-			while ((mockTextEditor.Text.Length < autoIndentSize) && (currentWait < maxWait)) {
+			while ((MockConsoleTextEditor.Text.Length < autoIndentSize) && (currentWait < maxWait)) {
 				Thread.Sleep(sleepInterval);
 				currentWait += sleepInterval;
 			}
 			
-			raiseKeyPressEvent = mockTextEditor.RaisePreviewKeyDownEvent(Key.A);
-			raiseDialogKeyPressEvent = mockTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
+			raiseKeyPressEvent = MockConsoleTextEditor.RaisePreviewKeyDownEvent(Key.A);
+			raiseDialogKeyPressEvent = MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
 			
 			currentWait = 0;
-			while ((mockTextEditor.Text.Length < autoIndentSize + 2) && (currentWait < maxWait)) {
+			while ((MockConsoleTextEditor.Text.Length < autoIndentSize + 2) && (currentWait < maxWait)) {
 				Thread.Sleep(10);
 				currentWait += sleepInterval;				
 			}
@@ -61,7 +58,7 @@ namespace RubyBinding.Tests.Console
 		[TestFixtureTearDown]
 		public void TearDown()
 		{
-			rubyConsole.Dispose();
+			TestableRubyConsole.Dispose();
 		}
 
 		[Test]
@@ -75,20 +72,20 @@ namespace RubyBinding.Tests.Console
 		public void ReadLineWithNonZeroAutoIndentSizeWritesSpacesToTextEditor()
 		{
 			string expectedString = String.Empty.PadLeft(initialAutoIndentSize) + "A\r\n";
-			Assert.AreEqual(expectedString, mockTextEditor.Text);
+			Assert.AreEqual(expectedString, MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void DocumentInsertCalledWhenAutoIndentIsNonZero()
 		{
-			Assert.IsTrue(mockTextEditor.IsWriteCalled);
+			Assert.IsTrue(MockConsoleTextEditor.IsWriteCalled);
 		}
 		
 		[Test]
 		public void NoTextWrittenWhenAutoIndentSizeIsZero()
 		{
-			MockConsoleTextEditor textEditor = new MockConsoleTextEditor();
-			RubyConsole console = new RubyConsole(textEditor, null);
+			TestableRubyConsole console = new TestableRubyConsole();
+			MockConsoleTextEditor textEditor = console.MockConsoleTextEditor;
 			textEditor.RaisePreviewKeyDownEvent(Key.A);
 			textEditor.RaisePreviewKeyDownEventForDialogKey(Key.Enter);
 			
@@ -98,7 +95,7 @@ namespace RubyBinding.Tests.Console
 		}
 		
 		/// <summary>
-		/// Should return false for any character that should be handled by the text editor itself.s
+		/// Should return false for any character that should be handled by the text editor itself.
 		/// </summary>
 		[Test]
 		public void RaiseKeyPressEventReturnedFalse()
@@ -107,7 +104,7 @@ namespace RubyBinding.Tests.Console
 		}
 		
 		/// <summary>
-		/// Should return false for any character that should be handled by the text editor itself.s
+		/// Should return false for any character that should be handled by the text editor itself.
 		/// </summary>
 		[Test]
 		public void RaiseDialogKeyPressEventReturnedFalse()
@@ -118,7 +115,7 @@ namespace RubyBinding.Tests.Console
 		void ReadLineFromConsoleOnDifferentThread()
 		{
 			System.Console.WriteLine("Reading on different thread");
-			readLine = rubyConsole.ReadLine(autoIndentSize);
+			readLine = TestableRubyConsole.ReadLine(autoIndentSize);
 			System.Console.WriteLine("Finished reading on different thread");
 		}
 	}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleSendLineTests.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleSendLineTests.cs
new file mode 100644
index 0000000000..7077260699
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleSendLineTests.cs
@@ -0,0 +1,174 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory;
+using ICSharpCode.RubyBinding;
+using Microsoft.Scripting.Hosting.Shell;
+using NUnit.Framework;
+using RubyBinding.Tests.Utils;
+
+namespace RubyBinding.Tests.Console
+{
+	[TestFixture]
+	public class RubyConsoleSendLineTests : RubyConsoleTestsBase
+	{
+		[Test]
+		public void SendLine_NoUnreadLines_AddsLineToUnreadLines()
+		{
+			SendLineToConsole("test");
+			string[] unreadLines = TestableRubyConsole.GetUnreadLines();
+			
+			string[] expectedUnreadlines = new string[] {"test"};
+			
+			Assert.AreEqual(expectedUnreadlines, unreadLines);
+		}
+		
+		void SendLineToConsole(string text)
+		{
+			base.CreateRubyConsole();
+			WritePrompt();
+			TestableRubyConsole.SendLine(text);
+		}
+		
+		void WritePrompt()
+		{
+			TestableRubyConsole.Write(">>> ", Style.Prompt);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_CreatesLockForPreviousLines()
+		{
+			SendLineToConsole("test");
+			List<string> lines = TestableRubyConsole.LockCreated.Lines;
+			List<string> expectedLines = TestableRubyConsole.GetUnreadLinesList();
+			
+			Assert.AreEqual(expectedLines, lines);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_LockForPreviousLinesIsDisposed()
+		{
+			SendLineToConsole("test");
+			bool disposed = TestableRubyConsole.LockCreated.IsDisposed;
+			
+			Assert.IsTrue(disposed);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_LineNotAddedBeforeLockCreated()
+		{
+			SendLineToConsole("test");
+			int count = TestableRubyConsole.LockCreated.UnreadLineCountWhenLockCreated;
+			int expectedCount = 0;
+			
+			Assert.AreEqual(expectedCount, count);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_LineAddedBeforeLockDisposed()
+		{
+			SendLineToConsole("test");
+			int count = TestableRubyConsole.LockCreated.UnreadLineCountWhenLockDisposed;
+			int expectedCount = 1;
+			
+			Assert.AreEqual(expectedCount, count);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_LineReceivedEventIsFired()
+		{
+			SendLineToConsole("test");
+			bool fired = TestableRubyConsole.IsLineReceivedEventFired;
+			Assert.IsTrue(fired);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_LineReceivedEventAfterLineAddedToUnreadLines()
+		{
+			SendLineToConsole("test");
+			int count = TestableRubyConsole.UnreadLineCountWhenLineReceivedEventFired;
+			int expectedCount = 1;
+			Assert.AreEqual(expectedCount, count);
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_LineWrittenToConsoleTextEditor()
+		{
+			SendLineToConsole("test");
+			string text = MockConsoleTextEditor.TextPassedToWrite;
+			string expectedTextWritten = "test\r\n";
+			
+			Assert.AreEqual(expectedTextWritten, text);
+		}
+		
+		[Test]
+		public void SendLine_TwoLinesInConsoleTextEditorCursorOnFirstLine_CursorMovedToEndOfLastLineBeforeTextWritten()
+		{
+			base.CreateRubyConsole();
+			WritePrompt();
+			MockConsoleTextEditor.Text = 
+				">>> first\r\n" +
+				">>> second\r\n" +
+				">>> ";
+			
+			MockConsoleTextEditor.Line = 0;
+			MockConsoleTextEditor.Column = 0;
+			TestableRubyConsole.SendLine("test");
+			
+			int expectedLine = 2;
+			int expectedColumn = 4;
+			Location expectedLocation = new Location(expectedColumn, expectedLine);
+			
+			Location location = MockConsoleTextEditor.CursorLocationWhenWriteTextCalled;
+			
+			Assert.AreEqual(expectedLocation, location);			
+		}
+		
+		[Test]
+		public void SendLine_NoUnreadLines_NoTextWrittenToConsoleTextEditorBeforeFirstPromptIsWritten()
+		{
+			base.CreateRubyConsole();
+			TestableRubyConsole.SendLine("test");
+			string text = MockConsoleTextEditor.TextPassedToWrite;
+			
+			Assert.IsNull(text);
+		}
+		
+		[Test]
+		public void Write_SendLineCalledButNoPromptWritten_WritesOutSavedSendLineText()
+		{
+			base.CreateRubyConsole();
+			TestableRubyConsole.SendLine("test");
+			
+			TestableRubyConsole.Write(">>> ", Style.Prompt);
+			string text = MockConsoleTextEditor.Text;
+			
+			string expectedText = 
+				">>> test\r\n";
+			Assert.AreEqual(expectedText, text);
+		}
+		
+		[Test]
+		public void Write_CalledTwiceAfterSendLineCalledButNoPromptWritten_WritesOutSavedSendLineTextOnlyOnce()
+		{
+			base.CreateRubyConsole();
+			TestableRubyConsole.SendLine("test");
+			
+			TestableRubyConsole.Write(">>> ", Style.Prompt);
+			TestableRubyConsole.Write(">>> ", Style.Prompt);
+
+			string text = MockConsoleTextEditor.Text;
+			
+			string expectedText = 
+				">>> test\r\n" +
+				">>> ";
+			Assert.AreEqual(expectedText, text);
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTestsBase.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTestsBase.cs
new file mode 100644
index 0000000000..0e71836196
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTestsBase.cs
@@ -0,0 +1,25 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.RubyBinding;
+using RubyBinding.Tests.Utils;
+
+namespace RubyBinding.Tests.Console
+{
+	public class RubyConsoleTestsBase
+	{
+		public MockConsoleTextEditor MockConsoleTextEditor;
+		public TestableRubyConsole TestableRubyConsole;
+		
+		public void CreateRubyConsole()
+		{
+			TestableRubyConsole = new TestableRubyConsole();
+			MockConsoleTextEditor = TestableRubyConsole.MockConsoleTextEditor;
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ConsoleTextEditorTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTextEditorTests.cs
similarity index 99%
rename from src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ConsoleTextEditorTestFixture.cs
rename to src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTextEditorTests.cs
index 5609af5468..ef8e3def96 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ConsoleTextEditorTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleTextEditorTests.cs
@@ -18,7 +18,7 @@ using RubyBinding.Tests.Utils;
 namespace RubyBinding.Tests.Console
 {
 	[TestFixture]
-	public class ConsoleTextEditorTestFixture
+	public class RubyConsoleTextEditorTests
 	{
 		RubyConsoleTextEditor consoleTextEditor;
 		TextEditor avalonEditTextEditor;
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleUnreadLinesTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleUnreadLinesTestFixture.cs
index db5c3fefda..5f2bd83a49 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleUnreadLinesTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleUnreadLinesTestFixture.cs
@@ -23,40 +23,37 @@ namespace RubyBinding.Tests.Console
 	/// Tests the RubyConsole's GetUnreadLines method.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleUnreadLinesTestFixture
+	public class RubyConsoleUnreadLinesTestFixture : RubyConsoleTestsBase
 	{
-		RubyConsole rubyConsole;
-		MockConsoleTextEditor textEditor;
-		
 		[SetUp]
 		public void Init()
 		{
-			textEditor = new MockConsoleTextEditor();
-			rubyConsole = new RubyConsole(textEditor, null);
+			base.CreateRubyConsole();
 		}
 		
 		[Test]
 		public void NoUnreadLinesAtStart()
 		{
-			Assert.AreEqual(0, rubyConsole.GetUnreadLines().Length);
+			Assert.AreEqual(0, TestableRubyConsole.GetUnreadLines().Length);
 		}
 	
 		[Test]
 		public void HasUnreadLines()
 		{
-			Assert.IsFalse(rubyConsole.IsLineAvailable);
+			Assert.IsFalse(TestableRubyConsole.IsLineAvailable);
 		}
 		
 		[Test]
 		public void AddOneLine()
 		{
-			textEditor.RaisePreviewKeyDownEvent(System.Windows.Input.Key.A);
-			textEditor.RaisePreviewKeyDownEventForDialogKey(System.Windows.Input.Key.Enter);
+			MockConsoleTextEditor.RaisePreviewKeyDownEvent(System.Windows.Input.Key.A);
+			MockConsoleTextEditor.RaisePreviewKeyDownEventForDialogKey(System.Windows.Input.Key.Enter);
 			
+			string[] lines = TestableRubyConsole.GetUnreadLines();
 			string[] expectedLines = new string[] {"A"};
 			
-			Assert.AreEqual(expectedLines, rubyConsole.GetUnreadLines());
-			Assert.IsTrue(rubyConsole.IsLineAvailable);
+			Assert.AreEqual(expectedLines, lines);
+			Assert.IsTrue(TestableRubyConsole.IsLineAvailable);
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleWriteTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleWriteTestFixture.cs
index aa60248556..6c0fdfba43 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleWriteTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/RubyConsoleWriteTestFixture.cs
@@ -18,45 +18,40 @@ namespace RubyBinding.Tests.Console
 	/// Tests that the RubyConsole Write method correctly update the text editor.
 	/// </summary>
 	[TestFixture]
-	public class RubyConsoleWriteTestFixture
+	public class RubyConsoleWriteTestFixture : RubyConsoleTestsBase
 	{
-		RubyConsole rubyConsole;
-		MockConsoleTextEditor mockTextEditor;
-		
 		[SetUp]
 		public void Init()
 		{
-			mockTextEditor = new MockConsoleTextEditor();
-			mockTextEditor.Text = String.Empty;			
-			rubyConsole = new RubyConsole(mockTextEditor, null);
+			base.CreateRubyConsole();
 		}
 		
 		[Test]
 		public void WriteLine()
 		{
-			rubyConsole.WriteLine();
-			Assert.AreEqual(Environment.NewLine, mockTextEditor.Text);
+			TestableRubyConsole.WriteLine();
+			Assert.AreEqual(Environment.NewLine, MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void WriteLineWithText()
 		{
-			rubyConsole.WriteLine("test", Style.Out);
-			Assert.AreEqual("test" + Environment.NewLine, mockTextEditor.Text);
+			TestableRubyConsole.WriteLine("test", Style.Out);
+			Assert.AreEqual("test" + Environment.NewLine, MockConsoleTextEditor.Text);
 		}	
 		
 		[Test]
 		public void TwoWrites()
 		{
-			rubyConsole.Write("a", Style.Out);
-			rubyConsole.Write("b", Style.Out);
-			Assert.AreEqual("ab", mockTextEditor.Text);
+			TestableRubyConsole.Write("a", Style.Out);
+			TestableRubyConsole.Write("b", Style.Out);
+			Assert.AreEqual("ab", MockConsoleTextEditor.Text);
 		}
 		
 		[Test]
 		public void DoesNotHasLinesWaitingToBeRead()
 		{
-			Assert.IsFalse(rubyConsole.IsLineAvailable);
+			Assert.IsFalse(TestableRubyConsole.IsLineAvailable);
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ConsoleTextEditorThreadSafetyTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ThreadSafeRubyConsoleTextEditorTests.cs
similarity index 90%
rename from src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ConsoleTextEditorThreadSafetyTestFixture.cs
rename to src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ThreadSafeRubyConsoleTextEditorTests.cs
index 8fc04863aa..f00304b20b 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ConsoleTextEditorThreadSafetyTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/ThreadSafeRubyConsoleTextEditorTests.cs
@@ -19,18 +19,18 @@ using RubyBinding.Tests.Utils.Tests;
 namespace RubyBinding.Tests.Console
 {
 	[TestFixture]
-	public class ConsoleTextEditorThreadSafetyTestFixture
+	public class ThreadSafeRubyConsoleTextEditorTests
 	{
-		RubyConsoleTextEditor consoleTextEditor;
-		TextEditor avalonEditTextEditor;
+		ThreadSafeRubyConsoleTextEditor consoleTextEditor;
+		MockConsoleTextEditor textEditor;
 		MockControlDispatcher dispatcher;
 		
 		[TestFixtureSetUp]
 		public void SetUpFixture()
 		{
-			avalonEditTextEditor = new TextEditor();
+			textEditor = new MockConsoleTextEditor();
 			dispatcher = new MockControlDispatcher();
-			consoleTextEditor = new RubyConsoleTextEditor(avalonEditTextEditor, dispatcher);
+			consoleTextEditor = new ThreadSafeRubyConsoleTextEditor(textEditor, dispatcher);
 		}
 		
 		[Test]
@@ -80,7 +80,7 @@ namespace RubyBinding.Tests.Console
 		{
 			dispatcher.CheckAccessReturnValue = false;
 			dispatcher.MethodInvoked = null;
-			avalonEditTextEditor.Text = "abcd";
+			textEditor.Text = "abcd";
 			
 			consoleTextEditor.Replace(0, 2, "12");
 			Assert.IsNotNull(dispatcher.MethodInvoked);
@@ -91,7 +91,7 @@ namespace RubyBinding.Tests.Console
 		{
 			dispatcher.CheckAccessReturnValue = false;
 			dispatcher.MethodInvokedArgs = null;
-			avalonEditTextEditor.Text = "abcd";
+			textEditor.Text = "abcd";
 			
 			consoleTextEditor.Replace(0, 2, "12");
 			object[] expectedArgs = new object[] { 0, 2, "12" };
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/TwoRubyConsoleLinesWaitingTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/TwoRubyConsoleLinesWaitingTestFixture.cs
index dede95c9fd..afd754bd08 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/TwoRubyConsoleLinesWaitingTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Console/TwoRubyConsoleLinesWaitingTestFixture.cs
@@ -29,7 +29,7 @@ namespace RubyBinding.Tests.Console
 	{
 		string line1;
 		string line2;
-		RubyConsole rubyConsole;
+		TestableRubyConsole rubyConsole;
 		bool lineAvailableBeforeFirstEnterKey;
 		bool lineAvailableAfterFirstEnterKey;
 		bool lineAvailableAtEnd;
@@ -37,9 +37,8 @@ namespace RubyBinding.Tests.Console
 		[TestFixtureSetUp]
 		public void SetUpFixture()
 		{
-			MockConsoleTextEditor textEditor = new MockConsoleTextEditor();
-			using (rubyConsole = new RubyConsole(textEditor, null)) {
-
+			using (rubyConsole = new TestableRubyConsole()) {
+				MockConsoleTextEditor textEditor = rubyConsole.MockConsoleTextEditor;
 				textEditor.RaisePreviewKeyDownEvent(Input.Key.A);
 				textEditor.RaisePreviewKeyDownEvent(Input.Key.B);
 				textEditor.RaisePreviewKeyDownEvent(Input.Key.C);
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertCSharpToRubyMenuCommandTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertCSharpToRubyMenuCommandTestFixture.cs
index 0aa8727e6d..4020b972d2 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertCSharpToRubyMenuCommandTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertCSharpToRubyMenuCommandTestFixture.cs
@@ -14,9 +14,6 @@ using RubyBinding.Tests.Utils;
 
 namespace RubyBinding.Tests.Converter
 {
-	/// <summary>
-	/// Tests the ConvertCSharpToRubyMenuCommand.
-	/// </summary>
 	[TestFixture]
 	public class ConvertCSharpToRubyMenuCommandTestFixture : ConvertToRubyMenuCommand
 	{
@@ -24,13 +21,12 @@ namespace RubyBinding.Tests.Converter
 		string defaultFileName;
 		string language;
 		string fileNamePassedToGetParseInformation;
-		MockEditableViewContent mockViewContent;
 		
 		[TestFixtureSetUp]
 		public void SetUpFixture()
 		{
-			mockViewContent = new MockEditableViewContent();
-			mockViewContent.Text = 
+			MockWorkbench workbench = MockWorkbench.CreateWorkbenchWithOneViewContent("test.cs");
+			workbench.ActiveMockEditableViewContent.Text = 
 				"class Foo\r\n" +
 				"{\r\n" +
 				"    public Foo()\r\n" +
@@ -38,17 +34,9 @@ namespace RubyBinding.Tests.Converter
 				"    }\r\n" +
 				"}";
 			
-			mockViewContent.PrimaryFileName = new FileName("test.cs");
-			
-			MockWorkbench workbench = new MockWorkbench();
-			MockWorkbenchWindow window = new MockWorkbenchWindow();
-			window.ActiveViewContent = mockViewContent;
-			workbench.ActiveWorkbenchWindow = window;
-			
-			MockTextEditorOptions options = new MockTextEditorOptions();
+			MockTextEditorOptions options = workbench.ActiveMockEditableViewContent.MockTextEditorOptions;
 			options.IndentationSize = 4;
 			options.ConvertTabsToSpaces = true;
-			mockViewContent.TextEditorOptions = options;
 			
 			Run(workbench);
 		}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertVBNetToRubyMenuCommandTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertVBNetToRubyMenuCommandTestFixture.cs
index 7029d28aec..c2ca74e109 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertVBNetToRubyMenuCommandTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Converter/ConvertVBNetToRubyMenuCommandTestFixture.cs
@@ -14,9 +14,6 @@ using RubyBinding.Tests.Utils;
 
 namespace RubyBinding.Tests.Converter
 {
-	/// <summary>
-	/// Tests the ConvertVBNetToRubyMenuCommand.
-	/// </summary>
 	[TestFixture]
 	public class ConvertVBNetToRubyMenuCommandTestFixture : ConvertToRubyMenuCommand
 	{
@@ -24,28 +21,20 @@ namespace RubyBinding.Tests.Converter
 		string defaultFileName;
 		string language;
 		string fileNamePassedToGetParseInformation;
-		MockEditableViewContent mockViewContent;
 		
-		[TestFixtureSetUp]
-		public void SetUpFixture()
+		[SetUp]
+		public void Init()
 		{
-			mockViewContent = new MockEditableViewContent();
-			mockViewContent.Text = 
+			MockWorkbench workbench = MockWorkbench.CreateWorkbenchWithOneViewContent("test.vb");
+			workbench.ActiveMockEditableViewContent.Text = 
 				"class Foo\r\n" +
 				"    Public Sub New\r\n" +
 				"    End Sub\r\n" +
 				"end class";
-			mockViewContent.PrimaryFileName = new FileName("test.vb");
 			
-			MockWorkbench workbench = new MockWorkbench();
-			MockWorkbenchWindow window = new MockWorkbenchWindow();
-			window.ActiveViewContent = mockViewContent;
-			workbench.ActiveWorkbenchWindow = window;
-			
-			MockTextEditorOptions options = new MockTextEditorOptions();
+			MockTextEditorOptions options = workbench.ActiveMockEditableViewContent.MockTextEditorOptions;	
 			options.ConvertTabsToSpaces = false;
 			options.IndentationSize = 2;
-			mockViewContent.TextEditorOptions = options;
 			
 			Run(workbench);
 		}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/DebugRunRubyCommandTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/DebugRunRubyCommandTestFixture.cs
index 40e75962c5..6cbe6ce246 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/DebugRunRubyCommandTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/DebugRunRubyCommandTestFixture.cs
@@ -24,15 +24,8 @@ namespace RubyBinding.Tests.Gui
 		[TestFixtureSetUp]
 		public void SetUpFixture()
 		{
-			// Create dummy view content with the Ruby script.
-			MockViewContent viewContent = new MockViewContent();
-			viewContent.PrimaryFileName = new FileName(@"C:\Projects\test.rb");
-			MockWorkbenchWindow workbenchWindow = new MockWorkbenchWindow();
-			workbenchWindow.ActiveViewContent = viewContent;
-			MockWorkbench workbench = new MockWorkbench();
-			workbench.ActiveWorkbenchWindow = workbenchWindow;
+			MockWorkbench workbench = MockWorkbench.CreateWorkbenchWithOneViewContent(@"C:\Projects\test.rb");
 
-			// Create the Ruby binding addin options.
 			Properties p = new Properties();
 			RubyAddInOptions options = new RubyAddInOptions(p);
 			options.RubyFileName = @"C:\IronRuby\ir.exe";
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/RunRubyCommandTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/RunRubyCommandTests.cs
similarity index 78%
rename from src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/RunRubyCommandTestFixture.cs
rename to src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/RunRubyCommandTests.cs
index 50dbd55dcc..0dc6be7cb1 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/RunRubyCommandTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/RunRubyCommandTests.cs
@@ -20,7 +20,7 @@ namespace RubyBinding.Tests.Gui
 	/// passing the filename of the Ruby script active in SharpDevelop.
 	/// </summary>
 	[TestFixture]
-	public class RunRubyCommandTestFixture
+	public class RunRubyCommandTests
 	{
 		MockDebugger debugger;
 		RunRubyCommand command;
@@ -28,15 +28,8 @@ namespace RubyBinding.Tests.Gui
 		[TestFixtureSetUp]
 		public void SetUpFixture()
 		{
-			// Create dummy view content with the Ruby script.
-			MockViewContent viewContent = new MockViewContent();
-			viewContent.PrimaryFileName = new FileName(@"C:\Projects\test.rb");
-			MockWorkbenchWindow workbenchWindow = new MockWorkbenchWindow();
-			workbenchWindow.ActiveViewContent = viewContent;
-			MockWorkbench workbench = new MockWorkbench();
-			workbench.ActiveWorkbenchWindow = workbenchWindow;
+			MockWorkbench workbench = MockWorkbench.CreateWorkbenchWithOneViewContent(@"C:\Projects\test.rb");
 
-			// Create the Ruby binding addin options.
 			Properties p = new Properties();
 			RubyAddInOptions options = new RubyAddInOptions(p);
 			options.RubyFileName = @"C:\IronRuby\ir.exe";
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/SendLineToRubyConsoleCommandTests.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/SendLineToRubyConsoleCommandTests.cs
new file mode 100644
index 0000000000..5ce0bdb5de
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Gui/SendLineToRubyConsoleCommandTests.cs
@@ -0,0 +1,93 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.RubyBinding;
+using NUnit.Framework;
+using RubyBinding.Tests.Utils;
+
+namespace RubyBinding.Tests.Gui
+{
+	[TestFixture]
+	public class SendLineToRubyConsoleCommandTests
+	{
+		SendLineToRubyConsoleCommand sendLineToConsoleCommand;
+		MockConsoleTextEditor fakeConsoleTextEditor;
+		MockTextEditor fakeTextEditor;
+		MockWorkbench workbench;
+		MockRubyConsole fakeConsole;
+		
+		[Test]
+		public void Run_SingleLineInTextEditor_FirstLineSentToRubyConsole()
+		{
+			CreateSendLineToConsoleCommand();
+			AddSingleLineToTextEditor("print 'hello'");
+			sendLineToConsoleCommand.Run();
+			
+			string text = fakeConsole.TextPassedToSendLine;
+			
+			string expectedText = "print 'hello'";
+			Assert.AreEqual(expectedText, text);
+		}
+		
+		void CreateSendLineToConsoleCommand()
+		{
+			workbench = MockWorkbench.CreateWorkbenchWithOneViewContent("test.rb");
+			fakeConsoleTextEditor = workbench.MockRubyConsolePad.MockConsoleTextEditor;
+			fakeConsole = workbench.MockRubyConsolePad.MockRubyConsole;
+			fakeTextEditor = workbench.ActiveMockEditableViewContent.MockTextEditor;
+			sendLineToConsoleCommand = new SendLineToRubyConsoleCommand(workbench);
+		}
+		
+		void AddSingleLineToTextEditor(string line)
+		{
+			fakeTextEditor.Document.Text = line;
+			fakeTextEditor.Caret.Line = 1;
+
+			SetTextToReturnFromTextEditorGetLine(line);
+		}
+		
+		void SetTextToReturnFromTextEditorGetLine(string line)
+		{
+			FakeDocumentLine documentLine = new FakeDocumentLine();
+			documentLine.Text = line;
+			fakeTextEditor.FakeDocument.DocumentLineToReturnFromGetLine = documentLine;			
+		}
+		
+		[Test]
+		public void Run_TwoLinesInTextEditorCursorOnFirstLine_FirstLineSentToRubyConsole()
+		{
+			CreateSendLineToConsoleCommand();
+			
+			fakeTextEditor.Document.Text = 
+				"print 'hello'\r\n" +
+				"print 'world'\r\n";
+			
+			fakeTextEditor.Caret.Line = 1;
+			
+			SetTextToReturnFromTextEditorGetLine("print 'hello'");
+			
+			sendLineToConsoleCommand.Run();
+			string text = fakeConsole.TextPassedToSendLine;
+			
+			string expectedText = "print 'hello'";
+			Assert.AreEqual(expectedText, text);
+		}
+		
+		[Test]
+		public void Run_SingleLineInTextEditor_RubyConsolePadBroughtToFront()
+		{
+			CreateSendLineToConsoleCommand();
+			AddSingleLineToTextEditor("print 'hello'");
+			
+			sendLineToConsoleCommand.Run();
+			
+			bool broughtToFront = workbench.MockRubyConsolePad.BringToFrontCalled;
+			Assert.IsTrue(broughtToFront);
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/LanguageBindingTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/LanguageBindingTestFixture.cs
deleted file mode 100644
index f7d68638da..0000000000
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/LanguageBindingTestFixture.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// <file>
-//     <copyright see="prj:///doc/copyright.txt"/>
-//     <license see="prj:///doc/license.txt"/>
-//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
-//     <version>$Revision$</version>
-// </file>
-
-using System;
-using ICSharpCode.RubyBinding;
-using ICSharpCode.SharpDevelop.Project;
-using ICSharpCode.SharpDevelop.Internal.Templates;
-using NUnit.Framework;
-
-namespace RubyBinding.Tests
-{
-	/// <summary>
-	/// Tests the RubyLanguageBinding class.
-	/// </summary>
-	[TestFixture]
-	public class LanguageBindingTestFixture
-	{
-		RubyLanguageBinding languageBinding;
-		RubyProject project;
-		
-		[TestFixtureSetUp]
-		public void SetUpFixture()
-		{
-			languageBinding = new RubyLanguageBinding();
-			ProjectCreateInformation createInfo = new ProjectCreateInformation();
-			createInfo.ProjectName = "Ruby";
-			createInfo.OutputProjectFileName = @"C:\Projects\Ruby.rbproj";
-			createInfo.Solution = new Solution();
-			project = languageBinding.CreateProject(createInfo) as RubyProject;
-		}
-		
-		[Test]
-		public void Language()
-		{
-			Assert.AreEqual("Ruby", languageBinding.Language);
-		}
-		
-		[Test]
-		public void IsRubyProject()
-		{
-			Assert.IsNotNull(project);
-		}
-
-		[Test]
-		public void ProjectName()
-		{
-			Assert.AreEqual("Ruby", project.Name);
-		}
-	}
-}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/RubyBinding.Tests.csproj b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/RubyBinding.Tests.csproj
index a441507ab5..e903cce751 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/RubyBinding.Tests.csproj
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/RubyBinding.Tests.csproj
@@ -74,7 +74,6 @@
     <Compile Include="Configuration\RubyAddInOptionsTestFixture.cs" />
     <Compile Include="Console\CodeCompletionTests.cs" />
     <Compile Include="Console\CommandLineHistoryTestFixture.cs" />
-    <Compile Include="Console\ConsoleTextEditorThreadSafetyTestFixture.cs" />
     <Compile Include="Console\DisposedRubyConsoleTestFixture.cs" />
     <Compile Include="Console\EmptyCommandLineHistoryTestFixture.cs" />
     <Compile Include="Console\InsertConsoleCompletionDataTestFixture.cs" />
@@ -89,10 +88,13 @@
     <Compile Include="Console\RubyConsoleHostTests.cs" />
     <Compile Include="Console\RubyConsoleReadOnlyRegionsTestFixture.cs" />
     <Compile Include="Console\RubyConsoleReadTestFixture.cs" />
+    <Compile Include="Console\RubyConsoleSendLineTests.cs" />
+    <Compile Include="Console\RubyConsoleTestsBase.cs" />
+    <Compile Include="Console\RubyConsoleTextEditorTests.cs" />
     <Compile Include="Console\RubyConsoleUnreadLinesTestFixture.cs" />
     <Compile Include="Console\RubyConsoleWriteTestFixture.cs" />
     <Compile Include="Console\RubyOutputStreamTestFixture.cs" />
-    <Compile Include="Console\ConsoleTextEditorTestFixture.cs" />
+    <Compile Include="Console\ThreadSafeRubyConsoleTextEditorTests.cs" />
     <Compile Include="Console\TwoRubyConsoleLinesWaitingTestFixture.cs" />
     <Compile Include="Converter\AddHandlerConversionTestFixture.cs" />
     <Compile Include="Converter\ArrayCastConversionTestFixture.cs" />
@@ -299,7 +301,8 @@
     <Compile Include="Gui\DebugRunRubyCommandTestFixture.cs" />
     <Compile Include="Gui\RubyIndentationTests.cs" />
     <Compile Include="Gui\RubyOptionsPanelTestFixture.cs" />
-    <Compile Include="Gui\RunRubyCommandTestFixture.cs" />
+    <Compile Include="Gui\RunRubyCommandTests.cs" />
+    <Compile Include="Gui\SendLineToRubyConsoleCommandTests.cs" />
     <Compile Include="Parsing\ParseClassWithBaseClassTestFixture.cs" />
     <Compile Include="Parsing\ParseMethodWithOptionalParametersTestFixture.cs" />
     <Compile Include="Parsing\ParseMethodWithParametersTestFixture.cs" />
@@ -347,6 +350,10 @@
     <Compile Include="Utils\DerivedRubyFormsDesignerDisplayBinding.cs" />
     <Compile Include="Utils\DerivedToolStripMenuItem.cs" />
     <Compile Include="Utils\DoublePropertyUserControl.cs" />
+    <Compile Include="Utils\FakeCaret.cs" />
+    <Compile Include="Utils\FakeDocument.cs" />
+    <Compile Include="Utils\FakeDocumentLine.cs" />
+    <Compile Include="Utils\FakeLock.cs" />
     <Compile Include="Utils\FooItemCollection.cs" />
     <Compile Include="Utils\MockComponentCreator.cs" />
     <Compile Include="Utils\MockConsoleTextEditorKeyEventArgs.cs" />
@@ -365,6 +372,8 @@
     <Compile Include="Utils\MockResourceReader.cs" />
     <Compile Include="Utils\MockResourceService.cs" />
     <Compile Include="Utils\MockResourceWriter.cs" />
+    <Compile Include="Utils\MockRubyConsole.cs" />
+    <Compile Include="Utils\MockRubyConsolePad.cs" />
     <Compile Include="Utils\MockRubyFileService.cs" />
     <Compile Include="Utils\MockSite.cs" />
     <Compile Include="Utils\MockConsoleTextEditor.cs" />
@@ -374,13 +383,13 @@
     <Compile Include="Utils\MockTypeResolutionService.cs" />
     <Compile Include="Utils\MockViewContent.cs" />
     <Compile Include="Utils\MockWorkbench.cs" />
-    <Compile Include="Utils\MockWorkbenchWindow.cs" />
     <Compile Include="Utils\MSBuildEngineHelper.cs" />
     <Compile Include="Utils\NullPropertyUserControl.cs" />
     <Compile Include="Utils\RubyBindingAddInFile.cs" />
     <Compile Include="Utils\RubyParserHelper.cs" />
     <Compile Include="Utils\RubySelectedTestsHelper.cs" />
     <Compile Include="Utils\SupportInitCustomControl.cs" />
+    <Compile Include="Utils\TestableRubyConsole.cs" />
     <Compile Include="Utils\Tests\AddInPathHelperTestFixture.cs" />
     <Compile Include="Utils\Tests\MockControlDispatcherTestFixture.cs" />
     <Compile Include="Utils\Tests\MockConsoleTextEditorTestFixture.cs" />
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeCaret.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeCaret.cs
new file mode 100644
index 0000000000..2ddf7d4052
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeCaret.cs
@@ -0,0 +1,53 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.SharpDevelop.Editor;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class FakeCaret : ITextEditorCaret
+	{
+		public event EventHandler PositionChanged;
+		
+		protected virtual void OnPositionChanged(EventArgs e)
+		{
+			if (PositionChanged != null) {
+				PositionChanged(this, e);
+			}
+		}
+		
+		public int Offset {
+			get {
+				throw new NotImplementedException();
+			}
+			set {
+				throw new NotImplementedException();
+			}
+		}
+		
+		public int Line { get; set; }
+		
+		public int Column {
+			get {
+				throw new NotImplementedException();
+			}
+			set {
+				throw new NotImplementedException();
+			}
+		}
+		
+		public ICSharpCode.NRefactory.Location Position {
+			get {
+				throw new NotImplementedException();
+			}
+			set {
+				throw new NotImplementedException();
+			}
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocument.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocument.cs
new file mode 100644
index 0000000000..739ae82aa9
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocument.cs
@@ -0,0 +1,139 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.IO;
+using System.Text;
+using ICSharpCode.NRefactory;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Editor;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class FakeDocument : IDocument
+	{		
+		#pragma warning disable 0067
+		public event EventHandler<TextChangeEventArgs> Changing;
+		public event EventHandler<TextChangeEventArgs> Changed;		
+		public event EventHandler TextChanged;
+		#pragma warning restore 0067
+		
+		public FakeDocumentLine DocumentLineToReturnFromGetLine;
+		public int LineNumberPassedToGetLine;
+		
+		public string Text { get; set; }
+		
+		public int TotalNumberOfLines {
+			get {
+				throw new NotImplementedException();
+			}
+		}
+		
+		public ITextBufferVersion Version {
+			get {
+				throw new NotImplementedException();
+			}
+		}
+		
+		public int TextLength {
+			get {
+				throw new NotImplementedException();
+			}
+		}
+		
+		public IDocumentLine GetLine(int lineNumber)
+		{
+			LineNumberPassedToGetLine = lineNumber;
+			return DocumentLineToReturnFromGetLine;
+		}
+		
+		public IDocumentLine GetLineForOffset(int offset)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public int PositionToOffset(int line, int column)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public Location OffsetToPosition(int offset)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public void Insert(int offset, string text)
+		{
+			throw new NotImplementedException();			
+		}
+		
+		public void Remove(int offset, int length)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public void Replace(int offset, int length, string newText)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public void StartUndoableAction()
+		{
+			throw new NotImplementedException();
+		}
+		
+		public void EndUndoableAction()
+		{
+			throw new NotImplementedException();
+		}
+		
+		public IDisposable OpenUndoGroup()
+		{
+			throw new NotImplementedException();
+		}
+		
+		public ITextAnchor CreateAnchor(int offset)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public ITextBuffer CreateSnapshot()
+		{
+			throw new NotImplementedException();
+		}
+		
+		public ITextBuffer CreateSnapshot(int offset, int length)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public TextReader CreateReader()
+		{
+			throw new NotImplementedException();
+		}
+		
+		public TextReader CreateReader(int offset, int length)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public char GetCharAt(int offset)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public string GetText(int offset, int length)
+		{
+			throw new NotImplementedException();
+		}
+		
+		public object GetService(Type serviceType)
+		{
+			throw new NotImplementedException();
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocumentLine.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocumentLine.cs
new file mode 100644
index 0000000000..2472c5d6e8
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeDocumentLine.cs
@@ -0,0 +1,23 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.SharpDevelop.Editor;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class FakeDocumentLine : IDocumentLine
+	{
+		public int Offset { get; set; }
+		public int Length { get; set; }
+		public int EndOffset { get; set; }
+		public int TotalLength { get; set; }
+		public int DelimiterLength { get; set; }
+		public int LineNumber { get; set; }		
+		public string Text { get; set; }
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeLock.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeLock.cs
new file mode 100644
index 0000000000..bba884a3c3
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/FakeLock.cs
@@ -0,0 +1,33 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.Collections.Generic;
+using ICSharpCode.RubyBinding;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class FakeLock : ILock
+	{
+		public List<string> Lines;
+		public bool IsDisposed;
+		public int UnreadLineCountWhenLockCreated = -1;
+		public int UnreadLineCountWhenLockDisposed = -1;
+		
+		public FakeLock(List<string> lines)
+		{
+			this.Lines = lines;
+			UnreadLineCountWhenLockCreated = lines.Count;
+		}
+		
+		public void Dispose()
+		{
+			UnreadLineCountWhenLockDisposed = Lines.Count;
+			IsDisposed = true;
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockConsoleTextEditor.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockConsoleTextEditor.cs
index c91908ee0e..d984fccda4 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockConsoleTextEditor.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockConsoleTextEditor.cs
@@ -8,77 +8,69 @@
 using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
-using System.Windows.Input;
 using System.Text;
+using System.Windows.Input;
 
+using ICSharpCode.NRefactory;
 using ICSharpCode.RubyBinding;
 
 namespace RubyBinding.Tests.Utils
 {
 	public class MockConsoleTextEditor : IConsoleTextEditor
 	{
-		StringBuilder previousLines = new StringBuilder();
-		StringBuilder lineBuilder = new StringBuilder();
-		bool writeCalled;
-		int column;
-		int selectionStart;
-		int selectionLength;
-		int line;
-		int totalLines = 1;
-		bool showCompletionWindowCalled;
-		bool completionWindowDisplayed;
-		bool makeReadOnlyCalled;
-		RubyConsoleCompletionDataProvider completionProvider;
+		public bool IsDisposed;
+		public bool IsWriteCalled;
+		
+		public bool IsShowCompletionWindowCalled;
+		public bool IsMakeCurrentContentReadOnlyCalled;
+		public RubyConsoleCompletionDataProvider CompletionProviderPassedToShowCompletionWindow;
+		public string TextPassedToWrite;
+		public string TextPassedToReplace;
+		public int LengthPassedToReplace = -1;
+		public int IndexPassedToReplace = -1;
+		public Location CursorLocationWhenWriteTextCalled;
+		public bool IsColumnChangedBeforeTextWritten;
+		
+		public StringBuilder PreviousLines = new StringBuilder();
+		public StringBuilder LineBuilder = new StringBuilder();
+				
+		public event ConsoleTextEditorKeyEventHandler PreviewKeyDown;
 		
 		public MockConsoleTextEditor()
 		{
+			TotalLines = 1;
 		}
-		
-		public event ConsoleTextEditorKeyEventHandler PreviewKeyDown;
-		
-		public void Write(string text)
+				
+		public void Dispose()
 		{
-			writeCalled = true;
-			lineBuilder.Append(text);
-			column += text.Length;
-		}
-		
-		public bool IsWriteCalled {
-			get { return writeCalled; }
-			set { writeCalled = value; }
-		}
-
-		public bool IsShowCompletionWindowCalled {
-			get { return showCompletionWindowCalled; }
+			IsDisposed = true;
 		}
 		
-		public bool IsMakeCurrentContentReadOnlyCalled {
-			get { return makeReadOnlyCalled; }
+		public void Write(string text)
+		{
+			TextPassedToWrite = text;
+			CursorLocationWhenWriteTextCalled = new Location(Column, Line);
+			IsWriteCalled = true;
+			LineBuilder.Append(text);
+			Column += text.Length;
 		}
 		
-		/// <summary>
-		/// Returns the code completion data provider passed to the ShowCompletionWindow method.
-		/// </summary>
-		public RubyConsoleCompletionDataProvider CompletionDataProvider {
-			get { return completionProvider; }
-		}
-
 		public string Text {
-			get { return previousLines.ToString() + lineBuilder.ToString(); }
+			get { return PreviousLines.ToString() + LineBuilder.ToString(); }
 			set {
-				previousLines = new StringBuilder();
-				lineBuilder = new StringBuilder();
-				totalLines = 1;
+				PreviousLines = new StringBuilder();
+				LineBuilder = new StringBuilder();
+				TotalLines = 1;
 				foreach (char ch in value) {
-					lineBuilder.Append(ch);
+					LineBuilder.Append(ch);
 					if (ch == '\n') {
-						totalLines++;
-						previousLines.Append(lineBuilder.ToString());
-						lineBuilder = new StringBuilder();
+						TotalLines++;
+						PreviousLines.Append(LineBuilder.ToString());
+						LineBuilder = new StringBuilder();
 					}
 				}
-				column = lineBuilder.Length;
-				selectionStart = column;
+				Column = LineBuilder.Length;
+				SelectionStart = Column;
 			}
 		}
 		
@@ -90,12 +82,12 @@ namespace RubyBinding.Tests.Utils
 				KeyConverter converter = new KeyConverter();
 				string text = converter.ConvertToString(key);
 				if (IsCursorAtEnd) {
-					lineBuilder.Append(text);
+					LineBuilder.Append(text);
 				} else {
-					lineBuilder.Insert(column, text);
+					LineBuilder.Insert(Column, text);
 				}
-				column++;
-				selectionStart = column;
+				Column++;
+				SelectionStart = Column;
 			}
 			return e.Handled;
 		}
@@ -107,6 +99,11 @@ namespace RubyBinding.Tests.Utils
 			}
 		}
 		
+		public void RaisePreviewKeyDownEvent(MockConsoleTextEditorKeyEventArgs e)
+		{
+			OnPreviewKeyDown(e);
+		}
+		
 		public bool RaisePreviewKeyDownEventForDialogKey(Key key)
 		{
 			MockConsoleTextEditorKeyEventArgs e = new MockConsoleTextEditorKeyEventArgs(key);
@@ -115,22 +112,22 @@ namespace RubyBinding.Tests.Utils
 				switch (key) {
 					case Key.Enter: {
 						if (IsCursorAtEnd) {
-							lineBuilder.Append(Environment.NewLine);
-							previousLines.Append(lineBuilder.ToString());
-							lineBuilder = new StringBuilder();
-							column = 0;
-							selectionStart = column;
+							LineBuilder.Append(Environment.NewLine);
+							PreviousLines.Append(LineBuilder.ToString());
+							LineBuilder = new StringBuilder();
+							Column = 0;
+							SelectionStart = Column;
 						} else {
-							int length = lineBuilder.Length;
-							string currentLine = lineBuilder.ToString();
-							previousLines.Append(currentLine.Substring(0, column) + Environment.NewLine);
-							lineBuilder = new StringBuilder();
-							lineBuilder.Append(currentLine.Substring(column));
-							column = length - column;
-							selectionStart = column;
+							int length = LineBuilder.Length;
+							string currentLine = LineBuilder.ToString();
+							PreviousLines.Append(currentLine.Substring(0, Column) + Environment.NewLine);
+							LineBuilder = new StringBuilder();
+							LineBuilder.Append(currentLine.Substring(Column));
+							Column = length - Column;
+							SelectionStart = Column;
 						}
-						totalLines++;
-						line++;
+						TotalLines++;
+						Line++;
 					}
 					break;
 					case Key.Back: {
@@ -138,13 +135,13 @@ namespace RubyBinding.Tests.Utils
 					}
 					break;
 					case Key.Left: {
-						column--;
-						selectionStart = column;
+						Column--;
+						SelectionStart = Column;
 					}
 					break;
 					case Key.Right: {
-						column++;
-						selectionStart = column;
+						Column++;
+						SelectionStart = Column;
 					}
 					break;
 				}
@@ -152,72 +149,54 @@ namespace RubyBinding.Tests.Utils
 			return e.Handled;
 		}
 		
-		public int Column {
-			get { return column; }
-			set { column = value; }
-		}
-		
-		public int SelectionStart {
-			get { return selectionStart; }
-			set { selectionStart = value; }
-		}
-		
-		public int SelectionLength {
-			get { return selectionLength; }
-			set { selectionLength = value; }
-		}
-		
-		public int Line {
-			get { return line; }
-			set { line = value; }
-		}
-
-		public int TotalLines {
-			get { return totalLines; }
-		}
+		public bool IsCompletionWindowDisplayed { get; set; }
+		public int Column { get; set; }		
+		public int SelectionStart { get; set; }
+		public int SelectionLength { get; set; }
+		public int Line { get; set; }
+		public int TotalLines { get; set; }
 		
 		public string GetLine(int index)
 		{
-			if (index == totalLines - 1) {
-				return lineBuilder.ToString();
+			if (index == TotalLines - 1) {
+				return LineBuilder.ToString();
 			}
 			return "aaaa";
 		}
 		
 		public void Replace(int index, int length, string text)
 		{
-			lineBuilder.Remove(index, length);
-			lineBuilder.Insert(index, text);
+			TextPassedToReplace = text;
+			IndexPassedToReplace = index;
+			LengthPassedToReplace = length;
+			
+			LineBuilder.Remove(index, length);
+			LineBuilder.Insert(index, text);
 		}
 		
 		public void ShowCompletionWindow(RubyConsoleCompletionDataProvider completionDataProvider)
 		{
-			showCompletionWindowCalled = true;
-			completionWindowDisplayed = true;
-			this.completionProvider = completionDataProvider;
+			IsShowCompletionWindowCalled = true;
+			IsCompletionWindowDisplayed = true;
+			this.CompletionProviderPassedToShowCompletionWindow = completionDataProvider;
 		}
-		
-		public bool IsCompletionWindowDisplayed {
-			get { return completionWindowDisplayed; }
-			set { completionWindowDisplayed = value; }
-		}
-		
+				
 		public void MakeCurrentContentReadOnly()
 		{
-			makeReadOnlyCalled = true;
+			IsMakeCurrentContentReadOnlyCalled = true;
 		}
 		
 		bool IsCursorAtEnd {
-			get { return column == lineBuilder.ToString().Length; }
+			get { return Column == LineBuilder.ToString().Length; }
 		}
 		
 		void OnBackspaceKeyPressed()
 		{
 			if (SelectionLength == 0) {
 				// Remove a single character to the left of the cursor position.
-				lineBuilder.Remove(Column - 1, 1);
+				LineBuilder.Remove(Column - 1, 1);
 			} else {
-				lineBuilder.Remove(SelectionStart, SelectionLength);
+				LineBuilder.Remove(SelectionStart, SelectionLength);
 			}
 		}
 	}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockEditableViewContent.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockEditableViewContent.cs
index d59d542413..134d8aeb2a 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockEditableViewContent.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockEditableViewContent.cs
@@ -18,30 +18,31 @@ namespace RubyBinding.Tests.Utils
 	/// </summary>
 	public class MockEditableViewContent : MockViewContent, IEditable, ITextEditorProvider
 	{
-		MockTextEditor textEditor = new MockTextEditor();
-		string text = String.Empty;
+		public MockTextEditor MockTextEditor = new MockTextEditor();
 		
 		public MockEditableViewContent()
 		{
+			Text = String.Empty;
 		}
 		
-		public string Text {
-			get { return text; }
-			set { text = value; }
-		}
+		public string Text { get; set; }
 		
 		public ITextBuffer CreateSnapshot()
 		{
-			return new StringTextBuffer(text);
+			return new StringTextBuffer(Text);
 		}
 		
 		public ITextEditorOptions TextEditorOptions {
-			get { return textEditor.Options; }
-			set { textEditor.Options = value; }
+			get { return MockTextEditor.Options; }
+		}
+		
+		public MockTextEditorOptions MockTextEditorOptions {
+			get { return MockTextEditor.MockTextEditorOptions; }
+			set { MockTextEditor.MockTextEditorOptions = value; }
 		}
 		
 		public ITextEditor TextEditor {
-			get { return textEditor; }
+			get { return MockTextEditor; }
 		}
 		
 		public IDocument GetDocumentForFile(OpenedFile file)
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsole.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsole.cs
new file mode 100644
index 0000000000..c0beacd8bd
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsole.cs
@@ -0,0 +1,22 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.RubyBinding;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class MockRubyConsole : IRubyConsole
+	{
+		public string TextPassedToSendLine;
+		
+		public void SendLine(string text)
+		{
+			TextPassedToSendLine = text;
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsolePad.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsolePad.cs
new file mode 100644
index 0000000000..6a7823b50c
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockRubyConsolePad.cs
@@ -0,0 +1,33 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.RubyBinding;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class MockRubyConsolePad : IRubyConsolePad
+	{
+		public MockConsoleTextEditor MockConsoleTextEditor = new MockConsoleTextEditor();
+		public MockRubyConsole MockRubyConsole = new MockRubyConsole();
+		
+		public bool BringToFrontCalled;
+		
+		public void BringToFront()
+		{
+			BringToFrontCalled = true;
+		}
+		
+		public IConsoleTextEditor ConsoleTextEditor {
+			get { return MockConsoleTextEditor; }
+		}
+		
+		public IRubyConsole RubyConsole {
+			get { return MockRubyConsole; }
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockTextEditor.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockTextEditor.cs
index 7ef7012c78..cef4e6153c 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockTextEditor.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockTextEditor.cs
@@ -13,11 +13,10 @@ namespace RubyBinding.Tests.Utils
 {
 	public class MockTextEditor : ITextEditor
 	{
-		ITextEditorOptions options;
+		public MockTextEditorOptions MockTextEditorOptions = new MockTextEditorOptions();
 		
-		public MockTextEditor()
-		{
-		}
+		public FakeDocument FakeDocument = new FakeDocument();
+		public FakeCaret FakeCaret = new FakeCaret();
 		
 		public event EventHandler SelectionChanged;
 		
@@ -44,20 +43,15 @@ namespace RubyBinding.Tests.Utils
 		}
 		
 		public IDocument Document {
-			get {
-				throw new NotImplementedException();
-			}
+			get { return FakeDocument; }
 		}
 		
 		public ITextEditorCaret Caret {
-			get {
-				throw new NotImplementedException();
-			}
+			get { return FakeCaret; }
 		}
 		
 		public ITextEditorOptions Options {
-			get { return options; }
-			set { options = value; }
+			get { return MockTextEditorOptions; }
 		}
 		
 		public ICSharpCode.SharpDevelop.ILanguageBinding Language {
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbench.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbench.cs
index 159821aefd..f133e8e34c 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbench.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbench.cs
@@ -8,168 +8,37 @@
 using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
+
 using ICSharpCode.Core;
+using ICSharpCode.RubyBinding;
 using ICSharpCode.SharpDevelop;
 using ICSharpCode.SharpDevelop.Gui;
 
 namespace RubyBinding.Tests.Utils
 {
-	/// <summary>
-	/// Dummy IWorkbench class.
-	/// </summary>
-	public class MockWorkbench : IWorkbench
-	{
-		public event EventHandler ActiveWorkbenchWindowChanged { add {} remove {} }
-		public event EventHandler ActiveViewContentChanged { add {} remove {} }
-		public event EventHandler ActiveContentChanged { add {} remove {} }
-		public event ViewContentEventHandler ViewOpened { add {} remove {} }
-		public event ViewContentEventHandler ViewClosed { add {} remove {} }
-		
-		public IWin32Window MainWin32Window {
-			get { return null; }
-		}
-		
-		public System.ComponentModel.ISynchronizeInvoke SynchronizingObject {
-			get { return null; }
-		}
-		
-		public System.Windows.Window MainWindow {
-			get { return null; }
-		}
-		
-		public IStatusBarService StatusBar {
-			get { throw new NotImplementedException(); }
-		}
-		
-		public string Title {
-			get {
-				throw new NotImplementedException();
-			}
-			set {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public ICollection<IViewContent> ViewContentCollection {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public ICollection<IViewContent> PrimaryViewContents {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public IList<IWorkbenchWindow> WorkbenchWindowCollection {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public IList<PadDescriptor> PadContentCollection {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public IWorkbenchWindow ActiveWorkbenchWindow {
-			get; set;
-		}
-		
-		public IViewContent ActiveViewContent {
-			get {
-				if (ActiveWorkbenchWindow != null)
-					return ActiveWorkbenchWindow.ActiveViewContent;
-				else
-					return null;
-			}
-		}
-		
-		public object ActiveContent {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public IWorkbenchLayout WorkbenchLayout {
-			get {
-				throw new NotImplementedException();
-			}
-			set {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public bool IsActiveWindow {
-			get {
-				throw new NotImplementedException();
-			}
-		}
+	public class MockWorkbench : IRubyWorkbench
+	{		
+		public MockRubyConsolePad MockRubyConsolePad = new MockRubyConsolePad();
+		public MockEditableViewContent ActiveMockEditableViewContent;
 		
-		public void Initialize()
+		public static MockWorkbench CreateWorkbenchWithOneViewContent(string fileName)
 		{
-			throw new NotImplementedException();
+			MockEditableViewContent viewContent = new MockEditableViewContent();
+			viewContent.PrimaryFileName = new FileName(fileName);
+						
+			MockWorkbench workbench = new MockWorkbench();
+			workbench.ActiveMockEditableViewContent = viewContent;
+			
+			return workbench;
 		}
 		
-		public void ShowView(IViewContent content)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void ShowView(IViewContent content, bool switchToOpenedView)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void ShowPad(PadDescriptor content)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void UnloadPad(PadDescriptor content)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public PadDescriptor GetPad(Type type)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void CloseAllViews()
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void RedrawAllComponents()
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void UpdateRenderer()
-		{
-			throw new NotImplementedException();
-		}
-		
-		public Properties CreateMemento()
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void SetMemento(Properties memento)
+		public IViewContent ActiveViewContent {
+			get { return ActiveMockEditableViewContent; }
+		}		
+				
+		public IRubyConsolePad GetRubyConsolePad()
 		{
-			throw new NotImplementedException();
-		}
-		
-		public bool FullScreen {
-			get {
-				throw new NotImplementedException();
-			}
-			set {
-				throw new NotImplementedException();
-			}
+			return MockRubyConsolePad;
 		}
 	}
 }
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbenchWindow.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbenchWindow.cs
deleted file mode 100644
index 3925013990..0000000000
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/MockWorkbenchWindow.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-// <file>
-//     <copyright see="prj:///doc/copyright.txt"/>
-//     <license see="prj:///doc/license.txt"/>
-//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
-//     <version>$Revision$</version>
-// </file>
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Windows.Forms;
-using ICSharpCode.Core;
-using ICSharpCode.SharpDevelop;
-using ICSharpCode.SharpDevelop.Gui;
-
-namespace RubyBinding.Tests.Utils
-{
-	/// <summary>
-	/// Dummy IWorkbenchWindow class.
-	/// </summary>
-	public class MockWorkbenchWindow : IWorkbenchWindow
-	{
-		IViewContent viewContent;
-		
-		public MockWorkbenchWindow()
-		{
-		}
-		
-		public event EventHandler WindowSelected;
-		public event EventHandler WindowDeselected;
-		public event EventHandler TitleChanged;
-		public event EventHandler CloseEvent;
-		public event EventHandler ActiveViewContentChanged;
-		
-		public string Title {
-			get {
-				throw new NotImplementedException();
-			}
-			set {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public bool IsDisposed {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public IViewContent ActiveViewContent {
-			get {
-				return viewContent;
-			}
-			set {
-				viewContent = value;
-			}
-		}
-		
-		public bool CloseWindow(bool force)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void SelectWindow()
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void RedrawContent()
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void SwitchView(int viewNumber)
-		{
-			throw new NotImplementedException();
-		}
-		
-		public void OnWindowDeselected(EventArgs e)
-		{
-			if (WindowDeselected != null) {
-				WindowDeselected(this, e);
-			}
-		}
-		
-		public void OnWindowSelected(EventArgs e)
-		{
-			if (WindowSelected != null) {
-				WindowSelected(this, e);
-			}
-		}
-		
-		public System.Windows.Media.ImageSource Icon {
-			get {
-				throw new NotImplementedException();
-			}
-			set {
-				throw new NotImplementedException();
-			}
-		}
-		
-		public IList<IViewContent> ViewContents {
-			get {
-				throw new NotImplementedException();
-			}
-		}
-		
-		protected virtual void OnTitleChanged(EventArgs e)
-		{
-			if (TitleChanged != null) {
-				TitleChanged(this, e);
-			}
-		}
-		
-		protected virtual void OnCloseEvent(EventArgs e)
-		{
-			if (CloseEvent != null) {
-				CloseEvent(this, e);
-			}
-		}
-		
-		protected virtual void OnActiveViewContentChanged(EventArgs e)
-		{
-			if (ActiveViewContentChanged != null) {
-				ActiveViewContentChanged(this, e);
-			}
-		}
-	}
-}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/TestableRubyConsole.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/TestableRubyConsole.cs
new file mode 100644
index 0000000000..f3d8772579
--- /dev/null
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/TestableRubyConsole.cs
@@ -0,0 +1,56 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.Collections.Generic;
+using ICSharpCode.RubyBinding;
+using IronRuby.Hosting;
+
+namespace RubyBinding.Tests.Utils
+{
+	public class TestableRubyConsole : RubyConsole
+	{
+		public MockConsoleTextEditor MockConsoleTextEditor;
+		public FakeLock LockCreated;
+		public bool IsLineReceivedEventFired;
+		public int UnreadLineCountWhenLineReceivedEventFired = -1;
+		
+		public TestableRubyConsole()
+			: this(new MockConsoleTextEditor(), new RubyCommandLine())
+		{
+		}
+		
+		TestableRubyConsole(IConsoleTextEditor consoleTextEditor, RubyCommandLine commandLine)
+			: base(consoleTextEditor)
+		{
+			CommandLine = commandLine;
+			MockConsoleTextEditor = (MockConsoleTextEditor)consoleTextEditor;
+		}
+		
+		public List<string> GetUnreadLinesList()
+		{
+			return base.unreadLines;
+		}
+		
+		public string[] GetUnreadLines()
+		{
+			return base.unreadLines.ToArray();
+		}
+		
+		protected override ILock CreateLock(List<string> lines)
+		{
+			LockCreated = new FakeLock(lines);
+			return LockCreated;
+		}
+		
+		protected override void FireLineReceivedEvent()
+		{
+			IsLineReceivedEventFired = true;
+			UnreadLineCountWhenLineReceivedEventFired = LockCreated.Lines.Count;
+		}
+	}
+}
diff --git a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/Tests/MockEditableViewContentTestFixture.cs b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/Tests/MockEditableViewContentTestFixture.cs
index 2446ba410b..e1b1bd52e0 100644
--- a/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/Tests/MockEditableViewContentTestFixture.cs
+++ b/src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Utils/Tests/MockEditableViewContentTestFixture.cs
@@ -42,7 +42,7 @@ namespace RubyBinding.Tests.Utils.Tests
 		public void CanReplaceTextEditorOptions()
 		{
 			MockTextEditorOptions options = new MockTextEditorOptions();
-			view.TextEditorOptions = options;
+			view.MockTextEditorOptions = options;
 			Assert.AreSame(options, view.TextEditor.Options);
 		}
 	}