Browse Source

VsCode debugger support for multiple scripts. License updated.

Xanathar 9 years ago
parent
commit
cbd6008ccd

+ 5 - 3
LICENSE

@@ -1,15 +1,17 @@
-Copyright (c) 2014, Marco Mastropaolo
+Copyright (c) 2014-2016, Marco Mastropaolo
 All rights reserved.
 
 Parts of the string library are based on the KopiLua project (https://github.com/NLua/KopiLua)
 Copyright (c) 2012 LoDC
 
-Debugger icons are from the Eclipse project (https://www.eclipse.org/).
+Visual Studio Code debugger code is based on code from Microsoft vscode-mono-debug project (https://github.com/Microsoft/vscode-mono-debug).
+Copyright (c) Microsoft Corporation - released under MIT license.
+
+Remote Debugger icons are from the Eclipse project (https://www.eclipse.org/).
 Copyright of The Eclipse Foundation
 
 The MoonSharp icon is (c) Isaac, 2014-2015
 
-
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 

+ 18 - 34
src/DevTools/VsCodeDebugger_Testbed/Program.cs

@@ -13,56 +13,43 @@ namespace VsCodeDebugger_Testbed
 {
 	class Program
 	{
-		const int DEFAULT_PORT = 41912;
-
 		public static void Main(string[] argv)
 		{
-			Script script = new Script();
-			MoonSharpVsCodeDebugServer server = new MoonSharpVsCodeDebugServer(script, DEFAULT_PORT);
-
-			script.AttachDebugger(server.GetDebugger());
-
-			script.DoFile(@"Z:/HDD/temp/lua/fact.lua");
+			MoonSharpVsCodeDebugServer server = new MoonSharpVsCodeDebugServer();
+			server.Start();
 
-			Closure func = script.Globals.Get("run").Function;
+			Script script1 = new Script();
+			script1.DoFile(@"Z:/HDD/temp/lua/fact.lua");
+			server.AttachToScript(script1, "Script #1");
+			Closure func1 = script1.Globals.Get("run").Function;
 
-			server.Start();
+			Script script2 = new Script();
+			script2.DoFile(@"Z:/HDD/temp/lua/fact2.lua");
+			server.AttachToScript(script2, "Script #2");
+			Closure func2 = script2.Globals.Get("run").Function;
 
 			Console.WriteLine("READY.");
+			int i = 0;
+
+			server.Current = null;
 
 			while (true)//(Console.ReadKey().Key != ConsoleKey.Escape)
 			{
 				if (Console.KeyAvailable)
-					break;
-
-				try
 				{
-					var val = func.Call(5);
-					Console.ForegroundColor = ConsoleColor.Magenta;
-					Console.WriteLine(val.Number);
-					System.Threading.Thread.Sleep(5000);
+					Console.ReadKey();
+					server.Detach(script2);
+					Console.WriteLine("Detached");
 				}
-				catch (InterpreterException ex)
-				{
-					Console.ForegroundColor = ConsoleColor.Red;
-					Console.Write(ex.DecoratedMessage);
-				}
-			}
 
-			script = new Script();
-			script.DoFile(@"Z:/HDD/temp/lua/fact2.lua");
-			func = script.Globals.Get("run").Function;
-			server.Rebind(script);
-
-			while (true)//(Console.ReadKey().Key != ConsoleKey.Escape)
-			{
+				Closure func = ((++i) % 2) == 0 ? func1 : func2;
 
 				try
 				{
 					var val = func.Call(5);
 					Console.ForegroundColor = ConsoleColor.Magenta;
 					Console.WriteLine(val.Number);
-					System.Threading.Thread.Sleep(5000);
+					System.Threading.Thread.Sleep(1000);
 				}
 				catch (InterpreterException ex)
 				{
@@ -71,8 +58,5 @@ namespace VsCodeDebugger_Testbed
 				}
 			}
 		}
-
-
-
 	}
 }

+ 1 - 1
src/MoonSharp.Interpreter/Script.cs

@@ -22,7 +22,7 @@ namespace MoonSharp.Interpreter
 		/// <summary>
 		/// The version of the MoonSharp engine
 		/// </summary>
-		public const string VERSION = "1.8.0.0";
+		public const string VERSION = "1.8.5.0";
 
 		/// <summary>
 		/// The Lua version being supported

+ 0 - 60
src/MoonSharp.VsCodeDebugger/DebuggerKit/BlockingQueue.cs

@@ -1,60 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-
-namespace MoonSharp.DebuggerKit
-{
-	// Taken from http://element533.blogspot.it/2010/01/stoppable-blocking-queue-for-net.html
-	internal class BlockingQueue<T>
-	{
-		private readonly Queue<T> _queue = new Queue<T>();
-		private bool _stopped;
-
-		public bool Enqueue(T item)
-		{
-			if (_stopped)
-				return false;
-			lock (_queue)
-			{
-				if (_stopped)
-					return false;
-				_queue.Enqueue(item);
-				Monitor.Pulse(_queue);
-			}
-			return true;
-		}
-
-		public T Dequeue()
-		{
-			if (_stopped)
-				return default(T);
-			lock (_queue)
-			{
-				if (_stopped)
-					return default(T);
-				while (_queue.Count == 0)
-				{
-					Monitor.Wait(_queue);
-					if (_stopped)
-						return default(T);
-				}
-				return _queue.Dequeue();
-			}
-		}
-
-		public void Stop()
-		{
-			if (_stopped)
-				return;
-			lock (_queue)
-			{
-				if (_stopped)
-					return;
-				_stopped = true;
-				Monitor.PulseAll(_queue);
-			}
-		}
-	}
-}

+ 22 - 16
src/MoonSharp.VsCodeDebugger/DebuggerKit/AsyncDebugger.cs → src/MoonSharp.VsCodeDebugger/DebuggerLogic/AsyncDebugger.cs

@@ -7,15 +7,16 @@ using System.Text.RegularExpressions;
 using System.Threading;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
+using MoonSharp.VsCodeDebugger;
 using MoonSharp.VsCodeDebugger.SDK;
 
-namespace MoonSharp.DebuggerKit
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
 {
 	internal class AsyncDebugger : IDebugger
 	{
-		public bool PauseRequested { get; set; }
+		private static object s_AsyncDebuggerIdLock = new object();
+		private static int s_AsyncDebuggerIdCounter = 0;
 
-		Script m_Script;
 		object m_Lock = new object();
 		private IAsyncDebuggerClient m_Client__;
 		DebuggerAction m_PendingAction = null;
@@ -31,12 +32,25 @@ namespace MoonSharp.DebuggerKit
 
 		public Regex ErrorRegex { get; set; }
 
-		public AsyncDebugger(Script script, Func<SourceCode, string> sourceFinder)
+		public Script Script { get; private set; }
+
+		public bool PauseRequested { get; set; }
+
+		public string Name { get; set; }
+
+		public int Id { get; private set; }
+
+
+		public AsyncDebugger(Script script, Func<SourceCode, string> sourceFinder, string name)
 		{
+			lock (s_AsyncDebuggerIdLock)
+				Id = s_AsyncDebuggerIdCounter++;
+
 			m_SourceFinder = sourceFinder;
 			ErrorRegex = new Regex(@"\A.*\Z");
-			m_Script = script;
+			Script = script;
 			m_WatchItems = new List<WatchItem>[(int)WatchType.MaxValue];
+			Name = name;
 
 			for (int i = 0; i < m_WatchItems.Length; i++)
 				m_WatchItems[i] = new List<WatchItem>(64);
@@ -52,7 +66,7 @@ namespace MoonSharp.DebuggerKit
 				{
 					if (value != null)
 					{
-						for (int i = 0; i < m_Script.SourceCodeCount; i++)
+						for (int i = 0; i < Script.SourceCodeCount; i++)
 							if (m_SourcesMap.ContainsKey(i))
 								value.OnSourceCodeChanged(i);
 					}
@@ -104,14 +118,6 @@ namespace MoonSharp.DebuggerKit
 			}
 		}
 
-		public void Unbind()
-		{
-			if (m_InGetActionLoop)
-				throw new Exception("Can't unbind if script is still running!!");
-
-			if (Client != null)
-				Client.Unbind();
-		}
 
 		public void QueueAction(DebuggerAction action)
 		{
@@ -132,11 +138,11 @@ namespace MoonSharp.DebuggerKit
 		{
 			try
 			{
-				return m_Script.CreateDynamicExpression(code);
+				return Script.CreateDynamicExpression(code);
 			}
 			catch (Exception ex)
 			{
-				return m_Script.CreateConstantDynamicExpression(code, DynValue.NewString(ex.Message));
+				return Script.CreateConstantDynamicExpression(code, DynValue.NewString(ex.Message));
 			}
 		}
 

+ 202 - 0
src/MoonSharp.VsCodeDebugger/DebuggerLogic/EmptyDebugSession.cs

@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter;
+using MoonSharp.VsCodeDebugger.SDK;
+
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
+{
+	internal class EmptyDebugSession : DebugSession
+	{
+		MoonSharpVsCodeDebugServer m_Server;
+		bool m_NotifyExecutionEnd = false;
+
+
+		internal EmptyDebugSession(MoonSharpVsCodeDebugServer server)
+			: base(true, false)
+		{
+			m_Server = server;
+		}
+
+		public override void Initialize(Response response, Table args)
+		{
+			SendText("Connected to MoonSharp {0} [{1}] on process {2} (PID {3})",
+					 Script.VERSION,
+					 Script.GlobalOptions.Platform.GetPlatformName(),
+					 System.Diagnostics.Process.GetCurrentProcess().ProcessName,
+					 System.Diagnostics.Process.GetCurrentProcess().Id);
+
+			SendText("No script is set as default for debugging; use the debug console to select the script to debug.\n");
+
+			SendList();
+
+			SendResponse(response, new Capabilities()
+			{
+				// This debug adapter does not need the configurationDoneRequest.
+				supportsConfigurationDoneRequest = false,
+
+				// This debug adapter does not support function breakpoints.
+				supportsFunctionBreakpoints = false,
+
+				// This debug adapter doesn't support conditional breakpoints.
+				supportsConditionalBreakpoints = false,
+
+				// This debug adapter does not support a side effect free evaluate request for data hovers.
+				supportsEvaluateForHovers = false,
+
+				// This debug adapter does not support exception breakpoint filters
+				exceptionBreakpointFilters = new object[0]
+			});
+
+			// Debugger is ready to accept breakpoints immediately
+			SendEvent(new InitializedEvent());
+		}
+
+		private void SendList()
+		{
+			int currId = m_Server.CurrentId ?? -1000;
+
+			SendText("==========================================================");
+
+			foreach (var pair in m_Server.GetAttachedDebuggersByIdAndName())
+			{
+				string isdef = (pair.Key == currId) ? " (default)" : "";
+				SendText("{0} : {1}{2}", pair.Key.ToString().PadLeft(9), pair.Value, isdef);
+			}
+			SendText("");
+			SendText("Type the number of the script to debug, or '!' to refresh");
+		}
+
+		public override void Attach(Response response, Table arguments)
+		{
+			SendResponse(response);
+		}
+
+		public override void Continue(Response response, Table arguments)
+		{
+			SendList();
+			SendResponse(response);
+		}
+
+		public override void Disconnect(Response response, Table arguments)
+		{
+			SendResponse(response);
+		}
+
+		private static string getString(Table args, string property, string dflt = null)
+		{
+			var s = (string)args[property];
+			if (s == null)
+			{
+				return dflt;
+			}
+			s = s.Trim();
+			if (s.Length == 0)
+			{
+				return dflt;
+			}
+			return s;
+		}
+
+		public override void Evaluate(Response response, Table args)
+		{
+			var expression = getString(args, "expression");
+			var context = getString(args, "context") ?? "hover";
+
+			if (context == "repl")
+				ExecuteRepl(expression);
+
+			SendResponse(response);
+		}
+
+		private void ExecuteRepl(string cmd)
+		{
+			int id = 0;
+			if (int.TryParse(cmd, out id))
+			{
+				m_Server.CurrentId = id;
+
+				SendText("Re-attach the debugger to debug the selected script.");
+
+				Unbind();
+			}
+			else
+			{
+				SendList();
+			}
+		}
+
+
+		public override void Launch(Response response, Table arguments)
+		{
+			SendResponse(response);
+		}
+
+		public override void Next(Response response, Table arguments)
+		{
+			SendList();
+			SendResponse(response);
+		}
+
+		public override void Pause(Response response, Table arguments)
+		{
+			SendList();
+			SendResponse(response);
+		}
+
+		public override void Scopes(Response response, Table arguments)
+		{
+			SendResponse(response);
+		}
+
+		public override void SetBreakpoints(Response response, Table args)
+		{
+			SendResponse(response);
+		}
+
+		public override void StackTrace(Response response, Table args)
+		{
+			SendResponse(response);
+		}
+
+
+		public override void StepIn(Response response, Table arguments)
+		{
+			SendList();
+			SendResponse(response);
+		}
+
+		public override void StepOut(Response response, Table arguments)
+		{
+			SendList();
+			SendResponse(response);
+		}
+
+		public override void Threads(Response response, Table arguments)
+		{
+			var threads = new List<Thread>() { new Thread(0, "Main Thread") };
+			SendResponse(response, new ThreadsResponseBody(threads));
+		}
+
+
+		public override void Variables(Response response, Table arguments)
+		{
+			SendResponse(response);
+		}
+
+
+		private void SendText(string msg, params object[] args)
+		{
+			msg = string.Format(msg, args);
+			SendEvent(new OutputEvent("console", msg + "\n"));
+		}
+
+
+		public void Unbind()
+		{
+			SendText("Bye.");
+			SendEvent(new TerminatedEvent());
+		}
+	}
+}

+ 1 - 1
src/MoonSharp.VsCodeDebugger/DebuggerKit/IAsyncDebuggerClient.cs → src/MoonSharp.VsCodeDebugger/DebuggerLogic/IAsyncDebuggerClient.cs

@@ -5,7 +5,7 @@ using System.Text;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
 
-namespace MoonSharp.DebuggerKit
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
 {
 	internal interface IAsyncDebuggerClient
 	{

+ 44 - 5
src/MoonSharp.VsCodeDebugger/MoonSharpDebugSession.cs → src/MoonSharp.VsCodeDebugger/DebuggerLogic/MoonSharpDebugSession.cs

@@ -4,17 +4,17 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
-using MoonSharp.DebuggerKit;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.VsCodeDebugger.SDK;
 
 
-namespace MoonSharp.VsCodeDebugger
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
 {
 	internal class MoonSharpDebugSession : DebugSession, IAsyncDebuggerClient
 	{
 		AsyncDebugger m_Debug;
+		MoonSharpVsCodeDebugServer m_Server;
 		List<DynValue> m_Variables = new List<DynValue>();
 		bool m_NotifyExecutionEnd = false;
 
@@ -22,9 +22,10 @@ namespace MoonSharp.VsCodeDebugger
 		const int SCOPE_SELF = 65537;
 
 
-		internal MoonSharpDebugSession(AsyncDebugger debugger)
+		internal MoonSharpDebugSession(MoonSharpVsCodeDebugServer server, AsyncDebugger debugger)
 			: base(true, false)
 		{
+			m_Server = server;
 			m_Debug = debugger;
 		}
 
@@ -36,6 +37,8 @@ namespace MoonSharp.VsCodeDebugger
 					 System.Diagnostics.Process.GetCurrentProcess().ProcessName,
 					 System.Diagnostics.Process.GetCurrentProcess().Id);
 
+			SendText("Debugging script '{0}'; use the debug console to debug another script.", m_Debug.Name);
+
 			SendText("Type '!help' in the Debug Console for available commands.");
 
 			SendResponse(response, new Capabilities()
@@ -163,6 +166,37 @@ namespace MoonSharp.VsCodeDebugger
 
 				SendText("Notifications of execution end are : {0}", m_NotifyExecutionEnd ? "enabled" : "disabled");
 			}
+			else if (cmd == "list")
+			{
+				int currId = m_Server.CurrentId ?? -1000;
+
+				foreach (var pair in m_Server.GetAttachedDebuggersByIdAndName())
+				{
+					string isthis = (pair.Key == m_Debug.Id) ? " (this)" : "";
+					string isdef = (pair.Key == currId) ? " (default)" : "";
+
+					SendText("{0} : {1}{2}{3}", pair.Key.ToString().PadLeft(9), pair.Value, isdef, isthis);
+				}
+			}
+			else if (cmd.StartsWith("select") || cmd.StartsWith("switch"))
+			{
+				string arg = cmd.Substring("switch".Length).Trim();
+
+				try
+				{
+					int id = int.Parse(arg);
+					m_Server.CurrentId = id;
+
+					if (cmd.StartsWith("switch"))
+						Unbind();
+					else
+						SendText("Next time you'll attach the debugger, it will be atteched to script #{0}", id);
+				}
+				catch (Exception ex)
+				{
+					SendText("Error setting regex: {0}", ex.Message);
+				}
+			}
 			else
 			{
 				SendText("Syntax error : {0}\n", cmd);
@@ -173,6 +207,9 @@ namespace MoonSharp.VsCodeDebugger
 			{
 				SendText("Available commands : ");
 				SendText("    !help - gets this help");
+				SendText("    !list - lists the other scripts which can be debugged");
+				SendText("    !select <id> - select another script for future sessions");
+				SendText("    !switch <id> - switch to another script (same as select + disconnect)");
 				SendText("    !seterror <regex> - sets the regex which tells which errors to trap");
 				SendText("    !geterror - gets the current value of the regex which tells which errors to trap");
 				SendText("    !execendnotify [on|off] - sets the notification of end of execution on or off (default = off)");
@@ -395,7 +432,8 @@ namespace MoonSharp.VsCodeDebugger
 		private void SendText(string msg, params object[] args)
 		{
 			msg = string.Format(msg, args);
-			SendEvent(new OutputEvent("console", DateTime.Now.ToString("u") + ": " + msg + "\n"));
+			// SendEvent(new OutputEvent("console", DateTime.Now.ToString("u") + ": " + msg + "\n"));
+			SendEvent(new OutputEvent("console", msg + "\n"));
 		}
 
 		public void OnException(ScriptRuntimeException ex)
@@ -405,7 +443,8 @@ namespace MoonSharp.VsCodeDebugger
 
 		public void Unbind()
 		{
-			SendText("Debug session closed from hosting process.");
+			SendText("Debug session has been closed by the hosting process.");
+			SendText("Bye.");
 			SendEvent(new TerminatedEvent());
 		}
 	}

+ 4 - 4
src/MoonSharp.VsCodeDebugger/MoonSharp.VsCodeDebugger.net35-client.csproj

@@ -45,12 +45,12 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="DebuggerKit\AsyncDebugger.cs" />
-    <Compile Include="DebuggerKit\IAsyncDebuggerClient.cs" />
-    <Compile Include="MoonSharpDebugSession.cs" />
+    <Compile Include="DebuggerLogic\AsyncDebugger.cs" />
+    <Compile Include="DebuggerLogic\IAsyncDebuggerClient.cs" />
+    <Compile Include="DebuggerLogic\EmptyDebugSession.cs" />
+    <Compile Include="DebuggerLogic\MoonSharpDebugSession.cs" />
     <Compile Include="MoonSharpVsCodeDebugServer.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="DebuggerKit\BlockingQueue.cs" />
     <Compile Include="SDK\DebugSession.cs" />
     <Compile Include="SDK\Protocol.cs" />
     <Compile Include="SDK\Utilities.cs" />

+ 228 - 32
src/MoonSharp.VsCodeDebugger/MoonSharpVsCodeDebugServer.cs

@@ -5,7 +5,8 @@ using System.Linq;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
-using MoonSharp.DebuggerKit;
+using System.Threading;
+using MoonSharp.VsCodeDebugger.DebuggerLogic;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.VsCodeDebugger.SDK;
@@ -15,91 +16,286 @@ namespace MoonSharp.VsCodeDebugger
 	/// <summary>
 	/// Class implementing a debugger allowing attaching from a Visual Studio Code debugging session.
 	/// </summary>
-	public class MoonSharpVsCodeDebugServer
+	public class MoonSharpVsCodeDebugServer : IDisposable
 	{
+		object m_Lock = new object();
+		List<AsyncDebugger> m_DebuggerList = new List<AsyncDebugger>();
+		AsyncDebugger m_Current = null;
+		ManualResetEvent m_StopEvent = new ManualResetEvent(false);
+		bool m_Started = false;
 		int m_Port;
-		AsyncDebugger m_Debugger;
-		Func<SourceCode, string> m_SourceFinder;
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="MoonSharpVsCodeDebugServer" /> class.
 		/// </summary>
+		/// <param name="port">The port on which the debugger listens. It's recommended to use 41912.</param>
+		public MoonSharpVsCodeDebugServer(int port = 41912)
+		{
+			m_Port = port;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="MoonSharpVsCodeDebugServer" /> class with a default script.
+		/// Note that for this specific script, it will NOT attach the debugger to the script.
+		/// </summary>
 		/// <param name="script">The script object to debug.</param>
 		/// <param name="port">The port on which the debugger listens. It's recommended to use 41912 unless you are going to keep more than one script object around.</param>
 		/// <param name="sourceFinder">A function which gets in input a source code and returns the path to
 		/// source file to use. It can return null and in that case (or if the file cannot be found)
 		/// a temporary file will be generated on the fly.</param>
+		[Obsolete("Use the constructor taking only a port, and the 'Attach' method instead.")]
 		public MoonSharpVsCodeDebugServer(Script script, int port, Func<SourceCode, string> sourceFinder = null)
 		{
 			m_Port = port;
-			m_SourceFinder = sourceFinder ?? (s => s.Name);
-			m_Debugger = new AsyncDebugger(script, m_SourceFinder);
+			m_Current = new AsyncDebugger(script, sourceFinder ?? (s => s.Name), "Default script");
+			m_DebuggerList.Add(m_Current);
 		}
 
+		/// <summary>
+		/// Attaches the specified script to the debugger
+		/// </summary>
+		/// <param name="script">The script.</param>
+		/// <param name="name">The name of the script.</param>
+		/// <param name="sourceFinder">A function which gets in input a source code and returns the path to
+		/// source file to use. It can return null and in that case (or if the file cannot be found)
+		/// a temporary file will be generated on the fly.</param>
+		/// <exception cref="ArgumentException">If the script has already been attached to this debugger.</exception>
+		public void AttachToScript(Script script, string name, Func<SourceCode, string> sourceFinder = null)
+		{
+			lock (m_Lock)
+			{
+				if (m_DebuggerList.Any(d => d.Script == script))
+					throw new ArgumentException("Script already attached to this debugger.");
+
+				var debugger = new AsyncDebugger(script, sourceFinder ?? (s => s.Name), name);
+				script.AttachDebugger(debugger);
+				m_DebuggerList.Add(debugger);
+
+				if (m_Current == null)
+					m_Current = debugger;
+			}
+		}
 
 		/// <summary>
-		/// Gets the debugger object (to register it).
+		/// Gets a list of the attached debuggers by id and name
 		/// </summary>
-		public IDebugger GetDebugger()
+		public IEnumerable<KeyValuePair<int, string>> GetAttachedDebuggersByIdAndName()
+		{
+			lock (m_Lock)
+				return m_DebuggerList
+					.OrderBy(d => d.Id)
+					.Select(d => new KeyValuePair<int, string>(d.Id, d.Name))
+					.ToArray();
+		}
+
+
+		/// <summary>
+		/// Gets or sets the current script by ID (see GetAttachedDebuggersByIdAndName). 
+		/// New vscode connections will attach to this debugger ID. Changing the current ID does NOT disconnect
+		/// connected clients.
+		/// </summary>
+		public int? CurrentId
+		{
+			get { lock (m_Lock) return m_Current != null ? m_Current.Id : (int?)null; }
+			set
+			{
+				lock (m_Lock)
+				{
+					if (value == null)
+					{
+						m_Current = null;
+						return;
+					}
+
+					var current = (m_DebuggerList.FirstOrDefault(d => d.Id == value));
+
+					if (current == null)
+						throw new ArgumentException("Cannot find debugger with given Id.");
+
+					m_Current = current;
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Gets or sets the current script. New vscode connections will attach to this script. Changing the current script does NOT disconnect
+		/// connected clients.
+		/// </summary>
+		public Script Current
+		{
+			get { lock(m_Lock) return m_Current != null ? m_Current.Script : null; }
+			set
+			{
+				lock (m_Lock)
+				{
+					if (value == null)
+					{
+						m_Current = null;
+						return;
+					}
+
+					var current = (m_DebuggerList.FirstOrDefault(d => d.Script == value));
+
+					if (current == null)
+						throw new ArgumentException("Cannot find debugger with given script associated.");
+
+					m_Current = current;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Detaches the specified script. The debugger attached to that script will get disconnected.
+		/// </summary>
+		/// <param name="script">The script.</param>
+		/// <exception cref="ArgumentException">Thrown if the script cannot be found.</exception>
+		public void Detach(Script script)
 		{
-			return m_Debugger;
+			lock (m_Lock)
+			{
+				var removed = m_DebuggerList.FirstOrDefault(d => d.Script == script);
+
+				if (removed == null)
+					throw new ArgumentException("Cannot detach script - not found.");
+
+				if (removed.Client != null)
+					removed.Client.Unbind();
+
+				m_DebuggerList.Remove(removed);
+
+				if (m_Current == removed)
+				{
+					if (m_DebuggerList.Count > 0)
+						m_Current = m_DebuggerList[m_DebuggerList.Count - 1];
+					else
+						m_Current = null;
+				}
+
+			}
 		}
 
-		private void RunSession(Stream inputStream, Stream outputStream)
+		/// <summary>
+		/// Gets or sets a delegate which will be called when logging messages are generated
+		/// </summary>
+		public Action<string> Logger { get; set; }
+
+		
+		/// <summary>
+		/// Gets the debugger object. Obsolete, use the new interface using the Attach method instead.
+		/// </summary>
+		[Obsolete("Use the Attach method instead.")]
+		public IDebugger GetDebugger()
 		{
-			MoonSharpDebugSession debugSession = new MoonSharpDebugSession(m_Debugger);
-			//debugSession.TRACE = trace_requests;
-			//debugSession.TRACE_RESPONSE = trace_responses;
-			debugSession.ProcessLoop(inputStream, outputStream);
+			lock(m_Lock)
+				return m_Current;
 		}
 
-		public void Rebind(Script script, Func<SourceCode, string> sourceFinder = null)
+		/// <summary>
+		/// Stops listening
+		/// </summary>
+		/// <exception cref="InvalidOperationException">Cannot stop; server was not started.</exception>
+		public void Dispose()
 		{
-			m_SourceFinder = sourceFinder ?? m_SourceFinder;
-			m_Debugger.Unbind();
-			m_Debugger = new AsyncDebugger(script, m_SourceFinder);
-			script.AttachDebugger(m_Debugger);
+			m_StopEvent.Set();
 		}
 
 		/// <summary>
 		/// Starts listening on the localhost for incoming connections.
 		/// </summary>
-		public void Start()
+		public MoonSharpVsCodeDebugServer Start()
 		{
-			TcpListener serverSocket = new TcpListener(IPAddress.Parse("127.0.0.1"), m_Port);
-			serverSocket.Start();
+			lock (m_Lock)
+			{
+				if (m_Started)
+					throw new InvalidOperationException("Cannot start; server has already been started.");
+
+				m_StopEvent.Reset();
+
+				TcpListener serverSocket = null;
+				serverSocket = new TcpListener(IPAddress.Parse("127.0.0.1"), m_Port);
+				serverSocket.Start();
+
+				SpawnThread("VsCodeDebugServer_" + m_Port.ToString(), () => ListenThread(serverSocket));
+
+				m_Started = true;
 
-			SpawnThread("VsCodeDebugServer", () =>
+				return this;
+			}
+		}
+
+
+		private void ListenThread(TcpListener serverSocket)
+		{
+			try
 			{
-				while (true)
+				while (!m_StopEvent.WaitOne(0))
 				{
 					var clientSocket = serverSocket.AcceptSocket();
 					if (clientSocket != null)
 					{
-						Console.Error.WriteLine(">> accepted connection from client");
+						string sessionId = Guid.NewGuid().ToString("N");
+						Log("[{0}] : Accepted connection from client {1}", sessionId, clientSocket.RemoteEndPoint);
 
-						SpawnThread("VsCodeDebugSession", () =>
+						SpawnThread("VsCodeDebugSession_" + sessionId, () =>
 						{
 							using (var networkStream = new NetworkStream(clientSocket))
 							{
 								try
 								{
-									RunSession(networkStream, networkStream);
+									RunSession(sessionId, networkStream);
 								}
-								catch (Exception e)
+								catch (Exception ex)
 								{
-									Console.Error.WriteLine("Exception: " + e);
+									Log("[{0}] : Error : {1}", ex.Message);
 								}
 							}
 							clientSocket.Close();
-							Console.Error.WriteLine(">> client connection closed");
+							Log("[{0}] : Client connection closed", sessionId);
 						});
 					}
 				}
-			});
+			}
+			catch (Exception e)
+			{
+				Log("Fatal error in listening thread : {0}", e.Message);
+			}
+			finally
+			{
+				if (serverSocket != null)
+					serverSocket.Stop();
+			}
 		}
 
-		private void SpawnThread(string name, Action threadProc)
+
+		private void RunSession(string sessionId, NetworkStream stream)
+		{
+			DebugSession debugSession = null;
+
+			lock (m_Lock)
+			{
+				if (m_Current != null)
+					debugSession = new MoonSharpDebugSession(this, m_Current);
+				else
+					debugSession = new EmptyDebugSession(this);
+			}
+
+			debugSession.ProcessLoop(stream, stream);
+		}
+
+		private void Log(string format, params object[] args)
+		{
+			Action<string> logger = Logger;
+
+			if (logger != null)
+			{
+				string msg = string.Format(format, args);
+				logger(msg);
+			}
+		}
+
+
+		private static void SpawnThread(string name, Action threadProc)
 		{
 			new System.Threading.Thread(() => threadProc())
 			{

+ 23 - 2
src/MoonSharp.VsCodeDebugger/SDK/DebugSession.cs

@@ -1,6 +1,27 @@
 /*---------------------------------------------------------------------------------------------
- *  Copyright (c) Microsoft Corporation. All rights reserved.
- *  Licensed under the MIT License. See License.txt in the project root for license information.
+Copyright (c) Microsoft Corporation
+
+All rights reserved. 
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
  *--------------------------------------------------------------------------------------------*/
 using System;
 using System.Collections.Generic;

+ 23 - 2
src/MoonSharp.VsCodeDebugger/SDK/Protocol.cs

@@ -1,6 +1,27 @@
 /*---------------------------------------------------------------------------------------------
- *  Copyright (c) Microsoft Corporation. All rights reserved.
- *  Licensed under the MIT License. See License.txt in the project root for license information.
+Copyright (c) Microsoft Corporation
+
+All rights reserved. 
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
  *--------------------------------------------------------------------------------------------*/
 using System;
 using System.Text;

+ 24 - 3
src/MoonSharp.VsCodeDebugger/SDK/Utilities.cs

@@ -1,6 +1,27 @@
 /*---------------------------------------------------------------------------------------------
- *  Copyright (c) Microsoft Corporation. All rights reserved.
- *  Licensed under the MIT License. See License.txt in the project root for license information.
+Copyright (c) Microsoft Corporation
+
+All rights reserved. 
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
  *--------------------------------------------------------------------------------------------*/
 using System;
 using System.Net;
@@ -11,7 +32,7 @@ using System.Reflection;
 
 namespace MoonSharp.VsCodeDebugger.SDK
 {
-	public class Utilities
+	internal class Utilities
 	{
 		private static readonly Regex VARIABLE = new Regex(@"\{(\w+)\}");