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.
 All rights reserved.
 
 
 Parts of the string library are based on the KopiLua project (https://github.com/NLua/KopiLua)
 Parts of the string library are based on the KopiLua project (https://github.com/NLua/KopiLua)
 Copyright (c) 2012 LoDC
 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
 Copyright of The Eclipse Foundation
 
 
 The MoonSharp icon is (c) Isaac, 2014-2015
 The MoonSharp icon is (c) Isaac, 2014-2015
 
 
-
 Redistribution and use in source and binary forms, with or without
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 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
 	class Program
 	{
 	{
-		const int DEFAULT_PORT = 41912;
-
 		public static void Main(string[] argv)
 		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.");
 			Console.WriteLine("READY.");
+			int i = 0;
+
+			server.Current = null;
 
 
 			while (true)//(Console.ReadKey().Key != ConsoleKey.Escape)
 			while (true)//(Console.ReadKey().Key != ConsoleKey.Escape)
 			{
 			{
 				if (Console.KeyAvailable)
 				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
 				try
 				{
 				{
 					var val = func.Call(5);
 					var val = func.Call(5);
 					Console.ForegroundColor = ConsoleColor.Magenta;
 					Console.ForegroundColor = ConsoleColor.Magenta;
 					Console.WriteLine(val.Number);
 					Console.WriteLine(val.Number);
-					System.Threading.Thread.Sleep(5000);
+					System.Threading.Thread.Sleep(1000);
 				}
 				}
 				catch (InterpreterException ex)
 				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>
 		/// <summary>
 		/// The version of the MoonSharp engine
 		/// The version of the MoonSharp engine
 		/// </summary>
 		/// </summary>
-		public const string VERSION = "1.8.0.0";
+		public const string VERSION = "1.8.5.0";
 
 
 		/// <summary>
 		/// <summary>
 		/// The Lua version being supported
 		/// 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 System.Threading;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.Interpreter.Debugging;
+using MoonSharp.VsCodeDebugger;
 using MoonSharp.VsCodeDebugger.SDK;
 using MoonSharp.VsCodeDebugger.SDK;
 
 
-namespace MoonSharp.DebuggerKit
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
 {
 {
 	internal class AsyncDebugger : IDebugger
 	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();
 		object m_Lock = new object();
 		private IAsyncDebuggerClient m_Client__;
 		private IAsyncDebuggerClient m_Client__;
 		DebuggerAction m_PendingAction = null;
 		DebuggerAction m_PendingAction = null;
@@ -31,12 +32,25 @@ namespace MoonSharp.DebuggerKit
 
 
 		public Regex ErrorRegex { get; set; }
 		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;
 			m_SourceFinder = sourceFinder;
 			ErrorRegex = new Regex(@"\A.*\Z");
 			ErrorRegex = new Regex(@"\A.*\Z");
-			m_Script = script;
+			Script = script;
 			m_WatchItems = new List<WatchItem>[(int)WatchType.MaxValue];
 			m_WatchItems = new List<WatchItem>[(int)WatchType.MaxValue];
+			Name = name;
 
 
 			for (int i = 0; i < m_WatchItems.Length; i++)
 			for (int i = 0; i < m_WatchItems.Length; i++)
 				m_WatchItems[i] = new List<WatchItem>(64);
 				m_WatchItems[i] = new List<WatchItem>(64);
@@ -52,7 +66,7 @@ namespace MoonSharp.DebuggerKit
 				{
 				{
 					if (value != null)
 					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))
 							if (m_SourcesMap.ContainsKey(i))
 								value.OnSourceCodeChanged(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)
 		public void QueueAction(DebuggerAction action)
 		{
 		{
@@ -132,11 +138,11 @@ namespace MoonSharp.DebuggerKit
 		{
 		{
 			try
 			try
 			{
 			{
-				return m_Script.CreateDynamicExpression(code);
+				return Script.CreateDynamicExpression(code);
 			}
 			}
 			catch (Exception ex)
 			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;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.Interpreter.Debugging;
 
 
-namespace MoonSharp.DebuggerKit
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
 {
 {
 	internal interface IAsyncDebuggerClient
 	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.Linq;
 using System.Text;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
-using MoonSharp.DebuggerKit;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.VsCodeDebugger.SDK;
 using MoonSharp.VsCodeDebugger.SDK;
 
 
 
 
-namespace MoonSharp.VsCodeDebugger
+namespace MoonSharp.VsCodeDebugger.DebuggerLogic
 {
 {
 	internal class MoonSharpDebugSession : DebugSession, IAsyncDebuggerClient
 	internal class MoonSharpDebugSession : DebugSession, IAsyncDebuggerClient
 	{
 	{
 		AsyncDebugger m_Debug;
 		AsyncDebugger m_Debug;
+		MoonSharpVsCodeDebugServer m_Server;
 		List<DynValue> m_Variables = new List<DynValue>();
 		List<DynValue> m_Variables = new List<DynValue>();
 		bool m_NotifyExecutionEnd = false;
 		bool m_NotifyExecutionEnd = false;
 
 
@@ -22,9 +22,10 @@ namespace MoonSharp.VsCodeDebugger
 		const int SCOPE_SELF = 65537;
 		const int SCOPE_SELF = 65537;
 
 
 
 
-		internal MoonSharpDebugSession(AsyncDebugger debugger)
+		internal MoonSharpDebugSession(MoonSharpVsCodeDebugServer server, AsyncDebugger debugger)
 			: base(true, false)
 			: base(true, false)
 		{
 		{
+			m_Server = server;
 			m_Debug = debugger;
 			m_Debug = debugger;
 		}
 		}
 
 
@@ -36,6 +37,8 @@ namespace MoonSharp.VsCodeDebugger
 					 System.Diagnostics.Process.GetCurrentProcess().ProcessName,
 					 System.Diagnostics.Process.GetCurrentProcess().ProcessName,
 					 System.Diagnostics.Process.GetCurrentProcess().Id);
 					 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.");
 			SendText("Type '!help' in the Debug Console for available commands.");
 
 
 			SendResponse(response, new Capabilities()
 			SendResponse(response, new Capabilities()
@@ -163,6 +166,37 @@ namespace MoonSharp.VsCodeDebugger
 
 
 				SendText("Notifications of execution end are : {0}", m_NotifyExecutionEnd ? "enabled" : "disabled");
 				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
 			else
 			{
 			{
 				SendText("Syntax error : {0}\n", cmd);
 				SendText("Syntax error : {0}\n", cmd);
@@ -173,6 +207,9 @@ namespace MoonSharp.VsCodeDebugger
 			{
 			{
 				SendText("Available commands : ");
 				SendText("Available commands : ");
 				SendText("    !help - gets this help");
 				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("    !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("    !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)");
 				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)
 		private void SendText(string msg, params object[] args)
 		{
 		{
 			msg = string.Format(msg, 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)
 		public void OnException(ScriptRuntimeException ex)
@@ -405,7 +443,8 @@ namespace MoonSharp.VsCodeDebugger
 
 
 		public void Unbind()
 		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());
 			SendEvent(new TerminatedEvent());
 		}
 		}
 	}
 	}

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

@@ -45,12 +45,12 @@
     <Reference Include="System.Xml" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   </ItemGroup>
   <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="MoonSharpVsCodeDebugServer.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="DebuggerKit\BlockingQueue.cs" />
     <Compile Include="SDK\DebugSession.cs" />
     <Compile Include="SDK\DebugSession.cs" />
     <Compile Include="SDK\Protocol.cs" />
     <Compile Include="SDK\Protocol.cs" />
     <Compile Include="SDK\Utilities.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;
 using System.Net.Sockets;
 using System.Net.Sockets;
 using System.Text;
 using System.Text;
-using MoonSharp.DebuggerKit;
+using System.Threading;
+using MoonSharp.VsCodeDebugger.DebuggerLogic;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.Interpreter.Debugging;
 using MoonSharp.VsCodeDebugger.SDK;
 using MoonSharp.VsCodeDebugger.SDK;
@@ -15,91 +16,286 @@ namespace MoonSharp.VsCodeDebugger
 	/// <summary>
 	/// <summary>
 	/// Class implementing a debugger allowing attaching from a Visual Studio Code debugging session.
 	/// Class implementing a debugger allowing attaching from a Visual Studio Code debugging session.
 	/// </summary>
 	/// </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;
 		int m_Port;
-		AsyncDebugger m_Debugger;
-		Func<SourceCode, string> m_SourceFinder;
 
 
 		/// <summary>
 		/// <summary>
 		/// Initializes a new instance of the <see cref="MoonSharpVsCodeDebugServer" /> class.
 		/// Initializes a new instance of the <see cref="MoonSharpVsCodeDebugServer" /> class.
 		/// </summary>
 		/// </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="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="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
 		/// <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)
 		/// 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>
 		/// 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)
 		public MoonSharpVsCodeDebugServer(Script script, int port, Func<SourceCode, string> sourceFinder = null)
 		{
 		{
 			m_Port = port;
 			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>
 		/// <summary>
-		/// Gets the debugger object (to register it).
+		/// Gets a list of the attached debuggers by id and name
 		/// </summary>
 		/// </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>
 		/// <summary>
 		/// Starts listening on the localhost for incoming connections.
 		/// Starts listening on the localhost for incoming connections.
 		/// </summary>
 		/// </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();
 					var clientSocket = serverSocket.AcceptSocket();
 					if (clientSocket != null)
 					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))
 							using (var networkStream = new NetworkStream(clientSocket))
 							{
 							{
 								try
 								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();
 							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())
 			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;
 using System.Collections.Generic;
 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;
 using System.Text;
 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;
 using System.Net;
 using System.Net;
@@ -11,7 +32,7 @@ using System.Reflection;
 
 
 namespace MoonSharp.VsCodeDebugger.SDK
 namespace MoonSharp.VsCodeDebugger.SDK
 {
 {
-	public class Utilities
+	internal class Utilities
 	{
 	{
 		private static readonly Regex VARIABLE = new Regex(@"\{(\w+)\}");
 		private static readonly Regex VARIABLE = new Regex(@"\{(\w+)\}");