diff --git a/Microsoft.DotNet.Try.Client/package-lock.json b/Microsoft.DotNet.Try.Client/package-lock.json index ae6558cdc..12fe1595c 100644 --- a/Microsoft.DotNet.Try.Client/package-lock.json +++ b/Microsoft.DotNet.Try.Client/package-lock.json @@ -4560,8 +4560,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4582,14 +4581,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4604,20 +4601,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4734,8 +4728,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4747,7 +4740,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4762,7 +4754,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4770,14 +4761,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4796,7 +4785,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4877,8 +4865,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4890,7 +4877,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4976,8 +4962,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5013,7 +4998,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5033,7 +5017,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5077,14 +5060,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -9743,8 +9724,7 @@ "version": "1.2.0", "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/Microsoft.DotNet.Try.Jupyter.Tests/ExecuteRequestHandlerTests.cs b/Microsoft.DotNet.Try.Jupyter.Tests/ExecuteRequestHandlerTests.cs index 7601b448a..5151a163b 100644 --- a/Microsoft.DotNet.Try.Jupyter.Tests/ExecuteRequestHandlerTests.cs +++ b/Microsoft.DotNet.Try.Jupyter.Tests/ExecuteRequestHandlerTests.cs @@ -48,7 +48,7 @@ public async Task handles_executeRequest() } [Fact] - public async Task sends_executeReply_message_on_codeSubmissionEvaluated() + public async Task sends_ExecuteReply_message_on_codeSubmissionEvaluated() { var kernel = new CSharpRepl(); @@ -83,7 +83,7 @@ public async Task sends_executeReply_with_error_message_on_codeSubmissionEvaluat } [Fact] - public async Task sends_executeResult_message_on_valueProduced() + public async Task sends_ExecuteReply_message_on_ValueProduced() { var kernel = new CSharpRepl(); @@ -99,5 +99,22 @@ public async Task sends_executeResult_message_on_valueProduced() .Should().Contain(message => message.Contains(MessageTypeValues.ExecuteResult)); } + + [Fact] + public async Task sends_ExecuteReply_message_when_submission_contains_only_a_directive() + { + var kernel = new CompositeKernel + { + new CSharpRepl() + }; + + var handler = new ExecuteRequestHandler(kernel); + var request = Message.Create(new ExecuteRequest("#kernel csharp"), null); + await handler.Handle(new JupyterRequestContext(_serverChannel, _ioPubChannel, request, _kernelStatus)); + + _serverRecordingSocket.DecodedMessages + .Should().Contain(message => + message.Contains(MessageTypeValues.ExecuteReply)); + } } } diff --git a/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs b/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs index 975ed862d..a86dc20b0 100644 --- a/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs +++ b/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs @@ -18,9 +18,8 @@ public class ExecuteRequestHandler : IDisposable { private readonly IKernel _kernel; private readonly RenderingEngine _renderingEngine; - private readonly ConcurrentDictionary _openRequests = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _openRequests = new ConcurrentDictionary(); private int _executionCount; - private readonly CodeSubmissionProcessors _processors; private readonly CompositeDisposable _disposables = new CompositeDisposable(); private class OpenRequest : IDisposable @@ -34,7 +33,6 @@ private class OpenRequest : IDisposable public OpenRequest(JupyterRequestContext context, ExecuteRequest executeRequest, int executionCount, Guid id, Dictionary transient) { - Context = context; ExecuteRequest = executeRequest; ExecutionCount = executionCount; @@ -61,7 +59,6 @@ public ExecuteRequestHandler(IKernel kernel) _renderingEngine.RegisterRenderer(typeof(IDictionary), new DictionaryRenderer()); _renderingEngine.RegisterRenderer(typeof(IList), new ListRenderer()); _renderingEngine.RegisterRenderer(typeof(IEnumerable), new SequenceRenderer()); - _processors = new CodeSubmissionProcessors(); } public async Task Handle(JupyterRequestContext context) @@ -73,15 +70,16 @@ public async Task Handle(JupyterRequestContext context) try { var command = new SubmitCode(executeRequest.Code, "csharp"); - command = await _processors.ProcessAsync(command); - var id = command.Id; + + var id = Guid.NewGuid(); + var transient = new Dictionary { { "display_id", id.ToString() } }; var openRequest = new OpenRequest(context, executeRequest, executionCount, id, transient); - _openRequests[id] = openRequest; + _openRequests[command] = openRequest; var kernelResult = await _kernel.SendAsync(command); - openRequest.AddDisposable(kernelResult.Events.Subscribe(OnKernelResultEvent)); + openRequest.AddDisposable(kernelResult.KernelEvents.Subscribe(OnKernelResultEvent)); } catch (Exception e) { @@ -118,7 +116,6 @@ public async Task Handle(JupyterRequestContext context) context.RequestHandlerStatus.SetAsIdle(); } } - void OnKernelResultEvent(IKernelEvent value) { @@ -142,9 +139,9 @@ void OnKernelResultEvent(IKernelEvent value) } } - private static void OnCodeSubmissionEvaluatedFailed(CodeSubmissionEvaluationFailed codeSubmissionEvaluationFailed, ConcurrentDictionary openRequests) + private static void OnCodeSubmissionEvaluatedFailed(CodeSubmissionEvaluationFailed codeSubmissionEvaluationFailed, ConcurrentDictionary openRequests) { - var openRequest = openRequests[codeSubmissionEvaluationFailed.ParentId]; + var openRequest = openRequests[codeSubmissionEvaluationFailed.Command]; var errorContent = new Error( eName: "Unhandled Exception", @@ -181,9 +178,9 @@ private static void OnCodeSubmissionEvaluatedFailed(CodeSubmissionEvaluationFail } private static void OnValueProduced(ValueProduced valueProduced, - ConcurrentDictionary openRequests, RenderingEngine renderingEngine) + ConcurrentDictionary openRequests, RenderingEngine renderingEngine) { - var openRequest = openRequests[valueProduced.ParentId]; + var openRequest = openRequests[valueProduced.Command]; try { var rendering = renderingEngine.Render(valueProduced.Value); @@ -232,9 +229,9 @@ private static void OnValueProduced(ValueProduced valueProduced, } private static void OnCodeSubmissionEvaluated(CodeSubmissionEvaluated codeSubmissionEvaluated, - ConcurrentDictionary openRequests) + ConcurrentDictionary openRequests) { - var openRequest = openRequests[codeSubmissionEvaluated.ParentId]; + var openRequest = openRequests[codeSubmissionEvaluated.Command]; // reply ok var executeReplyPayload = new ExecuteReplyOk(executionCount: openRequest.ExecutionCount); diff --git a/Microsoft.DotNet.Try.js/package-lock.json b/Microsoft.DotNet.Try.js/package-lock.json index 32e72fbf3..fb8dadc16 100644 --- a/Microsoft.DotNet.Try.js/package-lock.json +++ b/Microsoft.DotNet.Try.js/package-lock.json @@ -5700,8 +5700,7 @@ "version": "1.2.0", "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs b/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs index 4552e42c0..14774d90b 100644 --- a/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs +++ b/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs @@ -2,9 +2,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.IO; using FluentAssertions; using System.Linq; using System.Threading.Tasks; +using Newtonsoft.Json; +using Recipes; using WorkspaceServer.Kernel; using Xunit; @@ -55,7 +58,7 @@ public async Task it_returns_exceptions_thrown_in_user_code() .Should() .BeOfType() .Which - .Error + .Exception .Should() .BeOfType(); } @@ -118,9 +121,6 @@ public async Task it_returns_the_result_of_a_null_expression() KernelEvents.OfType() .Last() - .Should() - .BeOfType() - .Which .Value .Should() .BeNull(); @@ -149,12 +149,36 @@ public async Task it_aggregates_multiple_submissions() KernelEvents.OfType() .Last() - .Should() - .BeOfType() - .Which .Value .Should() .Be(3); } + + [Fact] + public async Task it_can_load_assembly_references_using_r_directive() + { + var kernel = await CreateKernelAsync(); + + var dll = new FileInfo(typeof(JsonConvert).Assembly.Location).FullName; + + await kernel.SendAsync( + new SubmitCode($"#r \"{dll}\"")); + await kernel.SendAsync( + new SubmitCode(@" +using Newtonsoft.Json; + +var json = JsonConvert.SerializeObject(new { value = ""hello"" }); + +json +")); + + KernelEvents.Should() + .ContainSingle(e => e is ValueProduced); + KernelEvents.OfType() + .Single() + .Value + .Should() + .Be(new { value = "hello" }.ToJson()); + } } } \ No newline at end of file diff --git a/WorkspaceServer.Tests/Kernel/CodeSubmissionProcessorTests.cs b/WorkspaceServer.Tests/Kernel/CodeSubmissionProcessorTests.cs deleted file mode 100644 index cb06eee01..000000000 --- a/WorkspaceServer.Tests/Kernel/CodeSubmissionProcessorTests.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.CommandLine; -using System.CommandLine.Invocation; -using System.Threading.Tasks; -using FluentAssertions; -using WorkspaceServer.Kernel; -using Xunit; - -namespace WorkspaceServer.Tests.Kernel -{ - public class CodeSubmissionProcessorTests - { - private readonly CodeSubmissionProcessors _processors; - - public CodeSubmissionProcessorTests() - { - _processors = new CodeSubmissionProcessors(); - } - [Fact] - public void can_register_processorHandlers() - { - var action = new Action(() => _processors.Add(new ReplaceAllProcessor())); - action.Should().NotThrow(); - _processors.ProcessorsCount.Should().BeGreaterThan(0); - } - - [Fact] - public async Task processing_code_submission_removes_processors() - { - _processors.Add(new PassThroughProcessor()); - var submission = new SubmitCode("#pass\nthis should remain"); - submission = await _processors.ProcessAsync(submission); - submission.Code.Should().NotContain("#pass") - .And.Contain("this should remain"); - } - - [Fact] - public async Task processing_code_submission_leaves_unprocessed_directives() - { - _processors.Add(new PassThroughProcessor()); - var submission = new SubmitCode("#pass\n#region code\nthis should remain\n#endregion"); - submission = await _processors.ProcessAsync(submission); - submission.Code.Should().NotContain("#pass") - .And.Match("*#region code\nthis should remain\n#endregion*"); - } - - - [Fact] - public async Task processing_code_submission_respect_directive_order() - { - _processors.Add(new AppendProcessor()); - var submission = new SubmitCode("#append --value PART1\n#append --value PART2\n#region code\nthis should remain\n#endregion"); - submission = await _processors.ProcessAsync(submission); - submission.Code.Should().NotContain("#pass") - .And.Match("*#region code\nthis should remain\n#endregion\nPART1\nPART2*"); - } - - private class ReplaceAllProcessor : ICodeSubmissionProcessor - { - public ReplaceAllProcessor() - { - Command = new Command("#replace", "replace submission with empty string"); - } - - public Command Command { get; } - - public Task ProcessAsync(SubmitCode codeSubmission) - { - codeSubmission.Code = string.Empty; - return Task.FromResult(codeSubmission); - } - } - - private class PassThroughProcessor : ICodeSubmissionProcessor - { - public PassThroughProcessor() - { - Command = new Command("#pass", "pass all code"); - } - - public Command Command { get; } - - public Task ProcessAsync(SubmitCode codeSubmission) - { - return Task.FromResult(codeSubmission); - } - } - - - private class AppendProcessor : ICodeSubmissionProcessor - { - private string _valueToAppend; - - private class AppendProcessorOptions - { - public string Value { get; } - - public AppendProcessorOptions(string value) - { - Value = value; - } - } - - public AppendProcessor() - { - Command = new Command("#append"); - var valueOption = new Option("--value") - { - Argument = new Argument() - }; - Command.AddOption(valueOption); - - Command.Handler = CommandHandler.Create((options) => - { - _valueToAppend = options.Value; - }); - } - - public Command Command { get; } - - public Task ProcessAsync(SubmitCode codeSubmission) - { - codeSubmission.Code = codeSubmission.Code + $"\n{_valueToAppend}"; - return Task.FromResult(codeSubmission); - } - } - } -} diff --git a/WorkspaceServer.Tests/Kernel/CompositeKernelTests.cs b/WorkspaceServer.Tests/Kernel/CompositeKernelTests.cs new file mode 100644 index 000000000..6a19bcb6c --- /dev/null +++ b/WorkspaceServer.Tests/Kernel/CompositeKernelTests.cs @@ -0,0 +1,126 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using FluentAssertions; +using System.Linq; +using System.Threading.Tasks; +using WorkspaceServer.Kernel; +using Xunit; +using Xunit.Abstractions; + +namespace WorkspaceServer.Tests.Kernel +{ + public class CompositeKernelTests + { + private ITestOutputHelper _output; + + public CompositeKernelTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact(Skip = "WIP")] + public void When_SubmitCode_command_adds_packages_to_fsharp_kernel_then_the_submission_is_passed_to_fsi() + { + throw new NotImplementedException(); + } + + [Fact(Skip = "WIP")] + public void When_SubmitCode_command_adds_packages_to_fsharp_kernel_then_PackageAdded_event_is_raised() + { + throw new NotImplementedException(); + } + + [Fact] + public async Task When_SubmitCode_command_adds_packages_to_csharp_kernel_then_the_submission_is_not_passed_to_csharpScript() + { + var kernel = new CompositeKernel + { + new CSharpRepl().UseNugetDirective() + }; + + var command = new SubmitCode("#r \"nuget:PocketLogger, 1.2.3\" \nvar a = new List();", "csharp"); + await kernel.SendAsync(command); + + command.Code.Should().Be("var a = new List();"); + } + + [Fact] + public async Task When_SubmitCode_command_adds_packages_to_csharp_kernel_then_PackageAdded_event_is_raised() + { + var kernel = new CompositeKernel + { + new CSharpRepl().UseNugetDirective() + }; + + var command = new SubmitCode("#r \"nuget:PocketLogger, 1.2.3\" \nvar a = new List();", "csharp"); + + var result = await kernel.SendAsync(command); + + var events = result.KernelEvents + .ToEnumerable() + .ToArray(); + + events + .Should() + .ContainSingle(e => e is NuGetPackageAdded); + + events.OfType() + .Single() + .PackageReference + .Should() + .BeEquivalentTo(new NugetPackageReference("PocketLogger", "1.2.3")); + } + + [Fact] + public async Task Kernel_can_be_chosen_by_specifying_kernel_name() + { + var receivedOnFakeRepl = new List(); + + var kernel = new CompositeKernel + { + new CSharpRepl(), + new FakeRepl("fake") + { + Handle = (command, context) => + { + receivedOnFakeRepl.Add(command); + return Task.CompletedTask; + } + } + }; + + await kernel.SendAsync(new SubmitCode("#kernel csharp")); + await kernel.SendAsync(new SubmitCode("var x = 123;")); + await kernel.SendAsync(new SubmitCode("#kernel fake")); + await kernel.SendAsync(new SubmitCode("hello!")); + await kernel.SendAsync(new SubmitCode("#kernel csharp")); + await kernel.SendAsync(new SubmitCode("x")); + + receivedOnFakeRepl.Should() + .BeEquivalentTo(new SubmitCode("hello!")); + } + } + + public class FakeRepl : KernelBase + { + private readonly string _name; + + public FakeRepl(string name) + { + _name = name; + } + + public override string Name => _name; + + public Func Handle { get; set; } + + protected override Task HandleAsync(IKernelCommand command, KernelPipelineContext context) + { + return Handle(command, context); + } + } +} \ No newline at end of file diff --git a/WorkspaceServer.Tests/Kernel/KernelCommandPipelineTests.cs b/WorkspaceServer.Tests/Kernel/KernelCommandPipelineTests.cs deleted file mode 100644 index bebb957e2..000000000 --- a/WorkspaceServer.Tests/Kernel/KernelCommandPipelineTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Threading.Tasks; -using FluentAssertions; -using WorkspaceServer.Kernel; -using Xunit; - -namespace WorkspaceServer.Tests.Kernel -{ - public class KernelCommandPipelineTests - { - [Fact(Skip = "WIP")] - public void When_SubmitCode_command_adds_packages_to_fsharp_kernel_then_the_submission_is_passed_to_fsi() - { - throw new NotImplementedException(); - } - - [Fact(Skip = "WIP")] - public void When_SubmitCode_command_adds_packages_to_fsharp_kernel_then_PackageAdded_event_is_raised() - { - throw new NotImplementedException(); - } - - [Fact(Skip = "WIP")] - public async Task When_SubmitCode_command_adds_packages_to_csharp_kernel_then_the_submission_is_not_passed_to_csharpScript() - { - var kernel = new CompositeKernel(new[] { new CSharpRepl() }); - - var command = new SubmitCode("#r \"nuget:PocketLogger, 1.2.3\" \nvar a = new List();", "csharp"); - await kernel.SendAsync(command); - command.Code.Should().Be("var a = new List();"); - } - - [Fact(Skip = "WIP")] - public void When_SubmitCode_command_adds_packages_to_csharp_kernel_then_PackageAdded_event_is_raised() - { - throw new NotImplementedException(); - } - } - -} \ No newline at end of file diff --git a/WorkspaceServer.Tests/Kernel/PackageReferenceTests.cs b/WorkspaceServer.Tests/Kernel/PackageReferenceTests.cs new file mode 100644 index 000000000..7f26ee61d --- /dev/null +++ b/WorkspaceServer.Tests/Kernel/PackageReferenceTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using FluentAssertions; +using WorkspaceServer.Kernel; +using Xunit; + +namespace WorkspaceServer.Tests.Kernel +{ + public class PackageReferenceTests + { + [Theory] + [InlineData("nuget:PocketLogger")] + [InlineData("nuget:PocketLogger,1.2.3")] + [InlineData("nuget:PocketLogger, 1.2.3")] + [InlineData("nuget:PocketLogger, 1.2.3-beta")] + public void Nuget_package_reference_correctly_parses_package_name(string value) + { + NugetPackageReference.TryParse(value, out var reference); + + reference.PackageName.Should().Be("PocketLogger"); + } + + [Theory] + [InlineData("nuget:PocketLogger", "")] + [InlineData("nuget:PocketLogger,1.2.3", "1.2.3")] + [InlineData("nuget:PocketLogger, 1.2.3", "1.2.3")] + [InlineData("nuget:PocketLogger, 1.2.3-beta", "1.2.3-beta")] + public void Nuget_package_reference_correctly_parses_package_version(string value, string expectedVersion) + { + NugetPackageReference.TryParse(value, out var reference); + + reference.PackageVersion.Should().Be(expectedVersion); + } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CSharpRepl.cs b/WorkspaceServer/Kernel/CSharpRepl.cs index de91c40e2..d3edd31cc 100644 --- a/WorkspaceServer/Kernel/CSharpRepl.cs +++ b/WorkspaceServer/Kernel/CSharpRepl.cs @@ -16,6 +16,8 @@ namespace WorkspaceServer.Kernel { public class CSharpRepl : KernelBase { + internal const string KernelName = "csharp"; + private static readonly MethodInfo _hasReturnValueMethod = typeof(Script) .GetMethod("HasReturnValue", BindingFlags.Instance | BindingFlags.NonPublic); @@ -26,18 +28,12 @@ public class CSharpRepl : KernelBase private StringBuilder _inputBuffer = new StringBuilder(); - public CSharpRepl() { SetupScriptOptions(); - SetupPipeline(); } - private void SetupPipeline() - { - - - } + public override string Name => KernelName; private void SetupScriptOptions() { @@ -55,39 +51,71 @@ private void SetupScriptOptions() typeof(Task<>).GetTypeInfo().Assembly); } - private async Task HandleCodeSubmission(SubmitCode codeSubmission, KernelCommandContext context) + private (bool shouldExecute, string completeSubmission) IsBufferACompleteSubmission(string input) + { + _inputBuffer.AppendLine(input); + + var code = _inputBuffer.ToString(); + var syntaxTree = SyntaxFactory.ParseSyntaxTree(code, ParseOptions); + + if (!SyntaxFactory.IsCompleteSubmission(syntaxTree)) + { + return (false, code); + } + + _inputBuffer = new StringBuilder(); + return (true, code); + } + + protected internal override async Task HandleAsync( + IKernelCommand command, + KernelPipelineContext context) { - var commandResult = new KernelCommandResult(); - commandResult.RelayEventsOn(PublishEvent); - context.Result = commandResult; - commandResult.OnNext(new CodeSubmissionReceived(codeSubmission.Id, codeSubmission.Code)); + switch (command) + { + case SubmitCode submitCode: + context.OnExecute(async invocationContext => + { + await HandleSubmitCode(submitCode, invocationContext); + }); - var (shouldExecute, code) = ComputeFullSubmission(codeSubmission.Code); + break; + } + } + + private async Task HandleSubmitCode( + SubmitCode codeSubmission, + KernelInvocationContext context) + { + var codeSubmissionReceived = new CodeSubmissionReceived( + codeSubmission.Code, + codeSubmission); + context.OnNext(codeSubmissionReceived); + + var (shouldExecute, code) = IsBufferACompleteSubmission(codeSubmission.Code); if (shouldExecute) { - commandResult.OnNext(new CompleteCodeSubmissionReceived(codeSubmission.Id)); + context.OnNext(new CompleteCodeSubmissionReceived(codeSubmission)); Exception exception = null; try { if (_scriptState == null) { _scriptState = await CSharpScript.RunAsync( - code, - ScriptOptions, - cancellationToken: context.CancellationToken); + code, + ScriptOptions); } else { _scriptState = await _scriptState.ContinueWithAsync( - code, - ScriptOptions, - e => - { - exception = e; - return true; - }, - context.CancellationToken); + code, + ScriptOptions, + e => + { + exception = e; + return true; + }); } } catch (Exception e) @@ -95,74 +123,34 @@ private async Task HandleCodeSubmission(SubmitCode codeSubmission, KernelCommand exception = e; } - var hasReturnValue = _scriptState != null && (bool)_hasReturnValueMethod.Invoke(_scriptState.Script, null); - - if (hasReturnValue) + if (HasReturnValue) { - commandResult.OnNext(new ValueProduced(codeSubmission.Id, _scriptState.ReturnValue)); + context.OnNext(new ValueProduced(_scriptState.ReturnValue, codeSubmission)); } + if (exception != null) { - var diagnostics = _scriptState?.Script?.GetDiagnostics() ?? Enumerable.Empty(); - if (diagnostics.Any()) - { - var message = string.Join("\n", diagnostics.Select(d => d.GetMessage())); + var message = string.Join("\n", (_scriptState?.Script?.GetDiagnostics() ?? + Enumerable.Empty()).Select(d => d.GetMessage())); - commandResult.OnNext(new CodeSubmissionEvaluationFailed(codeSubmission.Id, exception, message)); - } - else - { - commandResult.OnNext(new CodeSubmissionEvaluationFailed(codeSubmission.Id, exception)); - - } - commandResult.OnError(exception); + context.OnNext(new CodeSubmissionEvaluationFailed(exception, message, codeSubmission)); + context.OnError(exception); } else { - commandResult.OnNext(new CodeSubmissionEvaluated(codeSubmission.Id)); - commandResult.OnCompleted(); + context.OnNext(new CodeSubmissionEvaluated(codeSubmission)); + context.OnCompleted(); } } else { - commandResult.OnNext(new IncompleteCodeSubmissionReceived(codeSubmission.Id)); - commandResult.OnCompleted(); - } - } - - private (bool shouldExecute, string completeSubmission) ComputeFullSubmission(string input) - { - _inputBuffer.AppendLine(input); - - var code = _inputBuffer.ToString(); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(code, ParseOptions); - - if (!SyntaxFactory.IsCompleteSubmission(syntaxTree)) - { - return (false, code); + context.OnNext(new IncompleteCodeSubmissionReceived(codeSubmission)); + context.OnCompleted(); } - - _inputBuffer = new StringBuilder(); - return (true, code); } - protected internal override Task HandleAsync(KernelCommandContext context) - { - switch (context.Command) - { - case SubmitCode submitCode: - if (submitCode.Language == "csharp") - { - return HandleCodeSubmission(submitCode, context); - } - else - { - return Task.CompletedTask; - } - - default: - return Task.CompletedTask; - } - } + private bool HasReturnValue => + _scriptState != null && + (bool) _hasReturnValueMethod.Invoke(_scriptState.Script, null); } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CSharpReplExtensions.cs b/WorkspaceServer/Kernel/CSharpReplExtensions.cs new file mode 100644 index 000000000..30ea82c6a --- /dev/null +++ b/WorkspaceServer/Kernel/CSharpReplExtensions.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.CommandLine; +using System.CommandLine.Invocation; + +namespace WorkspaceServer.Kernel +{ + public static class CSharpReplExtensions + { + public static CSharpRepl UseNugetDirective(this CSharpRepl repl) + { + var packageRefArg = new Argument((SymbolResult result, out NugetPackageReference reference) => + NugetPackageReference.TryParse(result.Token.Value, out reference)) + { + Name = "package" + }; + + var r = new Command("#r") + { + packageRefArg + }; + + r.Handler = CommandHandler.Create(async (package, pipelineContext) => + { + pipelineContext.OnExecute(async invocationContext => + { + invocationContext.OnNext(new NuGetPackageAdded(package)); + invocationContext.OnCompleted(); + }); + }); + + repl.AddDirective(r); + + return repl; + } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CodeSubmissionEvaluated.cs b/WorkspaceServer/Kernel/CodeSubmissionEvaluated.cs index 3e480b9fd..ed6bc0a81 100644 --- a/WorkspaceServer/Kernel/CodeSubmissionEvaluated.cs +++ b/WorkspaceServer/Kernel/CodeSubmissionEvaluated.cs @@ -7,7 +7,7 @@ namespace WorkspaceServer.Kernel { public class CodeSubmissionEvaluated : KernelEventBase { - public CodeSubmissionEvaluated(Guid parentId) : base(parentId) + public CodeSubmissionEvaluated(IKernelCommand command) : base(command) { } } diff --git a/WorkspaceServer/Kernel/CodeSubmissionEvaluationFailed.cs b/WorkspaceServer/Kernel/CodeSubmissionEvaluationFailed.cs index ef9b1a2a0..bf675507d 100644 --- a/WorkspaceServer/Kernel/CodeSubmissionEvaluationFailed.cs +++ b/WorkspaceServer/Kernel/CodeSubmissionEvaluationFailed.cs @@ -1,5 +1,4 @@ - -// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; @@ -8,15 +7,19 @@ namespace WorkspaceServer.Kernel { public class CodeSubmissionEvaluationFailed : KernelEventBase { - public object Error { get; } - public string Message { get; } - - public CodeSubmissionEvaluationFailed(Guid parentId, object error, string message = null): base(parentId) + public CodeSubmissionEvaluationFailed( + Exception exception, + string message, + SubmitCode submitCode) : base(submitCode) { - Error = error; - Message = string.IsNullOrWhiteSpace(message) - ? error is Exception exception ? exception.Message : error.ToString() - : message; + Exception = exception; + Message = string.IsNullOrWhiteSpace(message) + ? exception.Message + : message; } + + public Exception Exception { get; } + + public string Message { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CodeSubmissionProcessorException.cs b/WorkspaceServer/Kernel/CodeSubmissionProcessorException.cs deleted file mode 100644 index e2594cabf..000000000 --- a/WorkspaceServer/Kernel/CodeSubmissionProcessorException.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; - -namespace WorkspaceServer.Kernel -{ - public class CodeSubmissionProcessorException : Exception - { - public SubmitCode CodeSubmission { get; } - - public CodeSubmissionProcessorException(Exception exception, SubmitCode codeSubmission) : base("CodeSubmission processing failed", exception) - { - CodeSubmission = codeSubmission; - } - } -} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CodeSubmissionProcessors.cs b/WorkspaceServer/Kernel/CodeSubmissionProcessors.cs deleted file mode 100644 index da6276e4a..000000000 --- a/WorkspaceServer/Kernel/CodeSubmissionProcessors.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.CommandLine.Builder; -using System.CommandLine.Invocation; -using System.Threading.Tasks; - -namespace WorkspaceServer.Kernel -{ - public class CodeSubmissionProcessors - { - private readonly RootCommand _rootCommand; - private readonly Dictionary _processors = new Dictionary(); - private Parser _parser; - - public int ProcessorsCount => _processors.Count; - - public CodeSubmissionProcessors() - { - _rootCommand = new RootCommand(); - _parser = new CommandLineBuilder(_rootCommand).Build(); - } - - public void Add(ICodeSubmissionProcessor processor) - { - _processors.Add(processor.Command, processor); - _rootCommand.AddCommand(processor.Command); - _parser = new CommandLineBuilder(_rootCommand).Build(); - } - - public async Task ProcessAsync(SubmitCode codeSubmission) - { - try - { - var lines = new Queue(codeSubmission.Code.Split(new[] {"\r\n", "\n"}, - StringSplitOptions.None)); - var unhandledLines = new Queue(); - while (lines.Count > 0) - { - var currentLine = lines.Dequeue(); - var result = _parser.Parse(currentLine); - - if (result.CommandResult != null && - _processors.TryGetValue(result.CommandResult.Command, out var processor)) - { - await _parser.InvokeAsync(result); - codeSubmission.Code = string.Join("\n", lines); - var newSubmission = await processor.ProcessAsync(codeSubmission); - lines = new Queue(newSubmission.Code.Split(new[] {"\r\n", "\n"}, - StringSplitOptions.None)); - } - else - { - unhandledLines.Enqueue(currentLine); - } - } - - codeSubmission.Code = string.Join("\n", unhandledLines); - return codeSubmission; - } - catch (Exception e) - { - throw new CodeSubmissionProcessorException(e, codeSubmission); - } - } - } -} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CodeSubmissionReceived.cs b/WorkspaceServer/Kernel/CodeSubmissionReceived.cs index 1827ab887..e9bd0625c 100644 --- a/WorkspaceServer/Kernel/CodeSubmissionReceived.cs +++ b/WorkspaceServer/Kernel/CodeSubmissionReceived.cs @@ -7,11 +7,11 @@ namespace WorkspaceServer.Kernel { public class CodeSubmissionReceived : KernelEventBase { - public string Value { get; } - - public CodeSubmissionReceived(Guid parentId, string value) : base(parentId) + public CodeSubmissionReceived(string value, SubmitCode submitCode) : base(submitCode) { Value = value ?? throw new ArgumentNullException(nameof(value)); } + + public string Value { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CompleteCodeSubmissionReceived.cs b/WorkspaceServer/Kernel/CompleteCodeSubmissionReceived.cs index 28bebe6bd..5fd825f19 100644 --- a/WorkspaceServer/Kernel/CompleteCodeSubmissionReceived.cs +++ b/WorkspaceServer/Kernel/CompleteCodeSubmissionReceived.cs @@ -7,7 +7,7 @@ namespace WorkspaceServer.Kernel { public class CompleteCodeSubmissionReceived : KernelEventBase { - public CompleteCodeSubmissionReceived(Guid parentId) : base(parentId) + public CompleteCodeSubmissionReceived(SubmitCode submitCode) : base(submitCode) { } } diff --git a/WorkspaceServer/Kernel/CompletionReceived.cs b/WorkspaceServer/Kernel/CompletionReceived.cs index afef8c1fa..488b23fc1 100644 --- a/WorkspaceServer/Kernel/CompletionReceived.cs +++ b/WorkspaceServer/Kernel/CompletionReceived.cs @@ -7,7 +7,7 @@ namespace WorkspaceServer.Kernel { public class CompletionReceived : KernelEventBase { - public CompletionReceived(Guid parentId) : base(parentId) + public CompletionReceived(IKernelCommand command) : base(command) { } } diff --git a/WorkspaceServer/Kernel/CompositeKernel.cs b/WorkspaceServer/Kernel/CompositeKernel.cs index 55c4c38b6..e1c5e2c26 100644 --- a/WorkspaceServer/Kernel/CompositeKernel.cs +++ b/WorkspaceServer/Kernel/CompositeKernel.cs @@ -2,34 +2,96 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Builder; +using System.CommandLine.Invocation; using System.Linq; -using System.Reactive.Linq; using System.Threading.Tasks; namespace WorkspaceServer.Kernel { - public class CompositeKernel : KernelBase + public class CompositeKernel : KernelBase, IEnumerable { - private readonly IReadOnlyList _kernels; - + private readonly List _kernels = new List(); + private readonly Argument _kernelNameArgument; - public CompositeKernel(IReadOnlyList kernels) + public CompositeKernel() { - _kernels = kernels ?? throw new ArgumentNullException(nameof(kernels)); - AddDisposable( kernels.Select(k => k.KernelEvents).Merge().Subscribe(PublishEvent)); + Pipeline.AddMiddleware(ChooseKernel); + + _kernelNameArgument = new Argument("kernelName"); + + var chooseKernelCommand = new Command("#kernel") + { + _kernelNameArgument + }; + + chooseKernelCommand.Handler = + CommandHandler.Create((kernelName, context) => + { + DefaultKernel = this.Single(k => k.Name == kernelName); + }); + + AddDirective(chooseKernelCommand); + } + + public IKernel DefaultKernel { get; set; } + + public override string Name => nameof(CompositeKernel); + + public void Add(IKernel kernel) + { + if (kernel == null) + { + throw new ArgumentNullException(nameof(kernel)); + } + + _kernels.Add(kernel); + + _kernelNameArgument.FromAmong(kernel.Name); + + AddDisposable(kernel.KernelEvents.Subscribe(PublishEvent)); } - protected internal override async Task HandleAsync(KernelCommandContext context) + private Task ChooseKernel( + IKernelCommand command, + KernelPipelineContext context, + KernelPipelineContinuation next) { - foreach (var kernel in _kernels.OfType()) + if (context.Kernel == null) { - await kernel.Pipeline.InvokeAsync(context); - if (context.Result != null) + if (DefaultKernel != null) + { + context.Kernel = DefaultKernel; + } + else if (_kernels.Count == 1) { - return; + context.Kernel = _kernels[0]; } } + + return next(command, context); + } + + protected internal override async Task HandleAsync( + IKernelCommand command, + KernelPipelineContext context) + { + var kernel = context.Kernel; + + if (kernel is KernelBase kernelBase) + { + await kernelBase.SendOnContextAsync(command, context); + return; + } + + throw new NoSuitableKernelException(); } + + public IEnumerator GetEnumerator() => _kernels.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/DiagnosticsReceived.cs b/WorkspaceServer/Kernel/DiagnosticsReceived.cs index 2cf0baba1..44cef6bfc 100644 --- a/WorkspaceServer/Kernel/DiagnosticsReceived.cs +++ b/WorkspaceServer/Kernel/DiagnosticsReceived.cs @@ -1,17 +1,11 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class DiagnosticsReceived : KernelEventBase { - public DiagnosticsReceived(IKernelCommand command) : this(command.Id) - { - } - - public DiagnosticsReceived(Guid parentId) : base(parentId) + public DiagnosticsReceived(IKernelCommand command) : base(command) { } } diff --git a/WorkspaceServer/Kernel/DocumentationReceived.cs b/WorkspaceServer/Kernel/DocumentationReceived.cs index 8ecb58c25..13925fa48 100644 --- a/WorkspaceServer/Kernel/DocumentationReceived.cs +++ b/WorkspaceServer/Kernel/DocumentationReceived.cs @@ -1,17 +1,11 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class DocumentationReceived : KernelEventBase { - public DocumentationReceived(IKernelCommand command) : this(command.Id) - { - } - - public DocumentationReceived(Guid parentId) : base(parentId) + public DocumentationReceived(IKernelCommand command) : base(command) { } } diff --git a/WorkspaceServer/Kernel/EmitProcessors.cs b/WorkspaceServer/Kernel/EmitProcessors.cs deleted file mode 100644 index 8cd13830f..000000000 --- a/WorkspaceServer/Kernel/EmitProcessors.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.CommandLine; -using System.CommandLine.Invocation; -using System.IO; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Scripting; - -namespace WorkspaceServer.Kernel -{ - public class EmitProcessors : ICodeSubmissionProcessor - { - private readonly Func _getScriptState; - - public EmitProcessors(Func getScriptState) - { - _getScriptState = getScriptState; - Command = new Command("emit"); - var outputOption = new Option("--output") - { - Argument = new Argument() - }; - - Command.AddOption(outputOption); - Command.Handler = CommandHandler.Create((options) => - { - if (!options.Output.Exists) - { - options.Output.Create(); - } - - var state = getScriptState(); - - var codeFile = new FileInfo(Path.Combine(options.Output.FullName, "code.cs")); - - using (var destination = codeFile.OpenWrite()) - using (var textWriter = new StreamWriter(destination)) - { - var source = state?.Script?.Code ?? string.Empty; - textWriter.Write($"// generated code\n{source}"); - } - - }); - } - public Task ProcessAsync(SubmitCode codeSubmission) - { - return Task.FromResult(codeSubmission); - } - - public Command Command { get; } - - private class EmitProcessorsOptions - { - public DirectoryInfo Output { get; } - - public EmitProcessorsOptions(DirectoryInfo output) - { - Output = output; - } - } - } -} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/IKernel.cs b/WorkspaceServer/Kernel/IKernel.cs index 6e5ad6875..a9a6fc4c8 100644 --- a/WorkspaceServer/Kernel/IKernel.cs +++ b/WorkspaceServer/Kernel/IKernel.cs @@ -7,9 +7,12 @@ namespace WorkspaceServer.Kernel { - public interface IKernel: IDisposable + public interface IKernel : IDisposable { + string Name { get; } + IObservable KernelEvents { get; } + Task SendAsync(IKernelCommand command, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/IKernelCommand.cs b/WorkspaceServer/Kernel/IKernelCommand.cs index b29a3096e..fc7b26a7f 100644 --- a/WorkspaceServer/Kernel/IKernelCommand.cs +++ b/WorkspaceServer/Kernel/IKernelCommand.cs @@ -1,34 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public interface IKernelCommand { - Guid ParentId { get; } - Guid Id { get; } - } - - public abstract class KernelCommandBase : IKernelCommand - { - protected KernelCommandBase() : this(Guid.NewGuid(), Guid.Empty) - { - - } - - protected KernelCommandBase(Guid id) : this(id,Guid.Empty) - { - } - - protected KernelCommandBase(Guid id, Guid parentId) - { - Id = id; - ParentId = parentId; - } - - public Guid ParentId { get; } - public Guid Id { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/IKernelCommandResult.cs b/WorkspaceServer/Kernel/IKernelCommandResult.cs index fae1cfbba..054160203 100644 --- a/WorkspaceServer/Kernel/IKernelCommandResult.cs +++ b/WorkspaceServer/Kernel/IKernelCommandResult.cs @@ -7,6 +7,6 @@ namespace WorkspaceServer.Kernel { public interface IKernelCommandResult { - IObservable Events { get; } + IObservable KernelEvents { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/IKernelEvent.cs b/WorkspaceServer/Kernel/IKernelEvent.cs index 06cf4275a..96dc6d414 100644 --- a/WorkspaceServer/Kernel/IKernelEvent.cs +++ b/WorkspaceServer/Kernel/IKernelEvent.cs @@ -1,69 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public interface IKernelEvent { - Guid ParentId { get; } - Guid Id { get; } - } - - public abstract class KernelEventBase : IKernelEvent - { - public Guid ParentId { get; } - public Guid Id { get; } - - protected KernelEventBase(Guid id, IKernelCommand command) : this(id, command.Id) - { - - } - - protected KernelEventBase(IKernelCommand command) : this(Guid.NewGuid(), command) - { - - } - - protected KernelEventBase(Guid parentId) : this(Guid.NewGuid(), parentId) - { - } - - protected KernelEventBase(Guid id, Guid parentId) - { - ParentId = parentId; - Id = id; - } - } - - - public class SendStandardInput : KernelCommandBase - { - } - - - /// - /// add a packages to the execution - /// - public class AddPackage : KernelCommandBase - { - } - - public class RequestCompletion : KernelCommandBase - { - } - - public class RequestDiagnostics : KernelCommandBase - { + IKernelCommand Command { get; } } - - public class RequestSignatureHelp : KernelCommandBase - { - } - - public class RequestDocumentation : KernelCommandBase - { - } - } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/IncompleteCodeSubmissionReceived.cs b/WorkspaceServer/Kernel/IncompleteCodeSubmissionReceived.cs index 0145e1673..943652b84 100644 --- a/WorkspaceServer/Kernel/IncompleteCodeSubmissionReceived.cs +++ b/WorkspaceServer/Kernel/IncompleteCodeSubmissionReceived.cs @@ -1,16 +1,12 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; - namespace WorkspaceServer.Kernel { public class IncompleteCodeSubmissionReceived : KernelEventBase { - public IncompleteCodeSubmissionReceived(Guid parentId): base(parentId) + public IncompleteCodeSubmissionReceived(SubmitCode submitCode) : base(submitCode) { - } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelBase.cs b/WorkspaceServer/Kernel/KernelBase.cs index 7f6b80536..c450832c1 100644 --- a/WorkspaceServer/Kernel/KernelBase.cs +++ b/WorkspaceServer/Kernel/KernelBase.cs @@ -2,6 +2,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Builder; +using System.CommandLine.Invocation; using System.Reactive.Disposables; using System.Reactive.Subjects; using System.Threading; @@ -9,29 +13,131 @@ namespace WorkspaceServer.Kernel { - public abstract class KernelBase: IKernel + public abstract class KernelBase : IKernel { public KernelCommandPipeline Pipeline { get; } private readonly Subject _channel = new Subject(); private readonly CompositeDisposable _disposables; - public IObservable KernelEvents => _channel; + private readonly List _directiveCommands = new List(); protected KernelBase() { - Pipeline = new KernelCommandPipeline(this); _disposables = new CompositeDisposable(); + + Pipeline = new KernelCommandPipeline(this); + + Pipeline.AddMiddleware(async (command, pipelineContext, next) => + { + switch (command) + { + case SubmitCode submitCode: + + var modified = false; + + var directiveParser = BuildDirectiveParser(pipelineContext); + + var lines = new Queue( + submitCode.Code.Split(new[] { "\r\n", "\n" }, + StringSplitOptions.None)); + + var unhandledLines = new List(); + + while (lines.Count > 0) + { + var currentLine = lines.Dequeue(); + + var parseResult = directiveParser.Parse(currentLine); + + if (parseResult.Errors.Count == 0) + { + modified = true; + await directiveParser.InvokeAsync(parseResult); + } + else + { + unhandledLines.Add(currentLine); + } + } + + var code = string.Join("\n", unhandledLines); + + if (modified) + { + if (string.IsNullOrWhiteSpace(code)) + { + pipelineContext.OnExecute(context => + { + context.OnNext(new CodeSubmissionEvaluated(submitCode)); + context.OnCompleted(); + return Task.CompletedTask; + }); + + return; + } + else + { + submitCode.Code = code; + } + } + + break; + } + + await next(command, pipelineContext); + }); } - public async Task SendAsync(IKernelCommand command, CancellationToken cancellationToken) + protected Parser BuildDirectiveParser(KernelPipelineContext pipelineContext) { - if (command == null) { - throw new ArgumentNullException(nameof(command)); + var root = new RootCommand(); + + foreach (var c in _directiveCommands) + { + root.Add(c); + } + + return new CommandLineBuilder(root) + .UseMiddleware( + context => context.BindingContext + .AddService( + typeof(KernelPipelineContext), + () => pipelineContext)) + .Build(); + } + + public IObservable KernelEvents => _channel; + + public abstract string Name { get; } + + public void AddDirective(Command command) + { + _directiveCommands.Add(command); + } + public async Task SendAsync( + IKernelCommand command, + CancellationToken cancellationToken) + { + if (command == null) + { + throw new ArgumentNullException(nameof(command)); } - var invocationContext = new KernelCommandContext(command, cancellationToken); - await Pipeline.InvokeAsync(invocationContext); - return invocationContext.Result; + + var pipelineContext = new KernelPipelineContext(PublishEvent); + + await SendOnContextAsync(command, pipelineContext); + + var result = await pipelineContext.InvokeAsync(); + + return result; + } + + public async Task SendOnContextAsync( + IKernelCommand command, + KernelPipelineContext invocationContext) + { + await Pipeline.InvokeAsync(command, invocationContext); } protected void PublishEvent(IKernelEvent kernelEvent) @@ -40,21 +146,24 @@ protected void PublishEvent(IKernelEvent kernelEvent) { throw new ArgumentNullException(nameof(kernelEvent)); } + _channel.OnNext(kernelEvent); } + protected void AddDisposable(IDisposable disposable) { if (disposable == null) { throw new ArgumentNullException(nameof(disposable)); } + _disposables.Add(disposable); } - protected internal abstract Task HandleAsync(KernelCommandContext context); - public void Dispose() - { - _disposables.Dispose(); - } + protected internal abstract Task HandleAsync( + IKernelCommand command, + KernelPipelineContext context); + + public void Dispose() => _disposables.Dispose(); } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelCommandBase.cs b/WorkspaceServer/Kernel/KernelCommandBase.cs new file mode 100644 index 000000000..1bab8c528 --- /dev/null +++ b/WorkspaceServer/Kernel/KernelCommandBase.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public abstract class KernelCommandBase : IKernelCommand + { + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelCommandContext.cs b/WorkspaceServer/Kernel/KernelCommandContext.cs deleted file mode 100644 index cb1d65de2..000000000 --- a/WorkspaceServer/Kernel/KernelCommandContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Threading; - -namespace WorkspaceServer.Kernel -{ - public class KernelCommandContext - { - public IKernelCommandResult Result { get; set; } - public IKernelCommand Command { get; } - public CancellationToken CancellationToken { get; } - - public KernelCommandContext(IKernelCommand command, CancellationToken cancellationToken) - { - Command = command; - CancellationToken = cancellationToken; - } - } -} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelCommandPipeline.cs b/WorkspaceServer/Kernel/KernelCommandPipeline.cs index 1d1db546a..dcf75d222 100644 --- a/WorkspaceServer/Kernel/KernelCommandPipeline.cs +++ b/WorkspaceServer/Kernel/KernelCommandPipeline.cs @@ -8,41 +8,56 @@ namespace WorkspaceServer.Kernel { - public class KernelCommandPipeline { + public class KernelCommandPipeline + { private readonly KernelBase _kernel; - private readonly List _invocations = new List(); + private readonly List _middlewares = new List(); + + private KernelCommandPipelineMiddleware _pipeline; public KernelCommandPipeline(KernelBase kernel) { _kernel = kernel ?? throw new ArgumentNullException(nameof(kernel)); } - public async Task InvokeAsync(KernelCommandContext context) + private void EnsureMiddlewarePipelineIsInitialized() + { + if (_pipeline == null) + { + _pipeline = BuildPipeline(); + } + } + + public async Task InvokeAsync( + IKernelCommand command, + KernelPipelineContext context) { - var invocationChain = BuildInvocationChain(); + EnsureMiddlewarePipelineIsInitialized(); - await invocationChain(context, invocationContext => Task.CompletedTask); + await _pipeline(command, context, (_, __) => Task.CompletedTask); } - private KernelCommandPipelineMiddleware BuildInvocationChain() + private KernelCommandPipelineMiddleware BuildPipeline() { - var invocations = new List(_invocations); + var invocations = new List(_middlewares); - invocations.Add(async (invocationContext, _) => + invocations.Add(async (command, context, _) => { - await _kernel.HandleAsync(invocationContext); + await _kernel.HandleAsync(command, context); }); return invocations.Aggregate( (function, continuation) => - (ctx, next) => - function(ctx, c => continuation(c, next))); + (cmd1, ctx1, next) => + function(cmd1, ctx1, (cmd2, ctx2) => + continuation(cmd2, ctx2, next))); } public void AddMiddleware(KernelCommandPipelineMiddleware middleware) { - _invocations.Add(middleware); + _middlewares.Add(middleware); + _pipeline = null; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelCommandPipelineMiddleware.cs b/WorkspaceServer/Kernel/KernelCommandPipelineMiddleware.cs index 56817974a..54e2cc3c0 100644 --- a/WorkspaceServer/Kernel/KernelCommandPipelineMiddleware.cs +++ b/WorkspaceServer/Kernel/KernelCommandPipelineMiddleware.cs @@ -1,12 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; using System.Threading.Tasks; namespace WorkspaceServer.Kernel { public delegate Task KernelCommandPipelineMiddleware( - KernelCommandContext context, - Func next); + IKernelCommand command, + KernelPipelineContext context, + KernelPipelineContinuation next); + + public delegate Task KernelCommandInvocation( + KernelInvocationContext context); } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelCommandResult.cs b/WorkspaceServer/Kernel/KernelCommandResult.cs index 741745893..93980b677 100644 --- a/WorkspaceServer/Kernel/KernelCommandResult.cs +++ b/WorkspaceServer/Kernel/KernelCommandResult.cs @@ -2,42 +2,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Reactive.Subjects; namespace WorkspaceServer.Kernel { - internal class KernelCommandResult : IKernelCommandResult, IObserver + internal class KernelCommandResult : IKernelCommandResult { - private readonly ReplaySubject _events; - private Action _eventRelay; - - public KernelCommandResult() - { - _events = new ReplaySubject(); - } - - public IObservable Events => _events; - - - public void OnCompleted() - { - _events.OnCompleted(); - } - - public void OnError(Exception error) + public KernelCommandResult(IObservable events) { - _events.OnError(error); + KernelEvents = events ?? throw new ArgumentNullException(nameof(events)); } - public void OnNext(IKernelEvent kernelEvent) - { - _events.OnNext(kernelEvent); - _eventRelay?.Invoke(kernelEvent); - } - - public void RelayEventsOn(Action eventRelay) - { - _eventRelay = eventRelay; - } + public IObservable KernelEvents { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelEventBase.cs b/WorkspaceServer/Kernel/KernelEventBase.cs new file mode 100644 index 000000000..de72ad54d --- /dev/null +++ b/WorkspaceServer/Kernel/KernelEventBase.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace WorkspaceServer.Kernel +{ + public abstract class KernelEventBase : IKernelEvent + { + protected KernelEventBase(IKernelCommand command) + { + Command = command ?? throw new ArgumentNullException(nameof(command)); + } + + protected KernelEventBase() + { + } + + public IKernelCommand Command { get; } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelExtensions.cs b/WorkspaceServer/Kernel/KernelExtensions.cs index 10780c2ec..3e87f13a8 100644 --- a/WorkspaceServer/Kernel/KernelExtensions.cs +++ b/WorkspaceServer/Kernel/KernelExtensions.cs @@ -8,7 +8,9 @@ namespace WorkspaceServer.Kernel { public static class KernelExtensions { - public static Task SendAsync(this IKernel kernel, IKernelCommand command) + public static Task SendAsync( + this IKernel kernel, + IKernelCommand command) { return kernel.SendAsync(command, CancellationToken.None); } diff --git a/WorkspaceServer/Kernel/KernelInvocationContext.cs b/WorkspaceServer/Kernel/KernelInvocationContext.cs new file mode 100644 index 000000000..bc775c9f8 --- /dev/null +++ b/WorkspaceServer/Kernel/KernelInvocationContext.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Reactive.Subjects; +using System.Threading.Tasks; + +namespace WorkspaceServer.Kernel +{ + public class KernelInvocationContext : IObserver + { + private readonly KernelCommandInvocation _invocation; + private readonly Action _publishEvent; + private readonly ReplaySubject _events = new ReplaySubject(); + + public KernelInvocationContext( + KernelCommandInvocation invocation, + Action publishEvent) + { + _invocation = invocation; + _publishEvent = publishEvent; + } + + public void OnCompleted() + { + _events.OnCompleted(); + } + + public void OnError(Exception exception) + { + _events.OnError(exception); + } + + public void OnNext(IKernelEvent @event) + { + _events.OnNext(@event); + _publishEvent(@event); + } + + internal IObservable KernelEvents => _events; + + public async Task InvokeAsync() + { + await _invocation(this); + } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/KernelPipelineContext.cs b/WorkspaceServer/Kernel/KernelPipelineContext.cs new file mode 100644 index 000000000..d08a1aa90 --- /dev/null +++ b/WorkspaceServer/Kernel/KernelPipelineContext.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Linq; +using System.Threading.Tasks; + +namespace WorkspaceServer.Kernel +{ + public class KernelPipelineContext + { + private readonly Action _publishEvent; + private readonly List _invocations = new List(); + + public KernelPipelineContext(Action publishEvent) + { + _publishEvent = publishEvent; + } + + internal IKernel Kernel { get; set; } + + public void OnExecute(KernelCommandInvocation invocation) + { + if (invocation == null) + { + throw new ArgumentNullException(nameof(invocation)); + } + + _invocations.Add(new KernelInvocationContext( + invocation, + _publishEvent)); + } + + internal async Task InvokeAsync() + { + var invocationContexts = _invocations.ToArray(); + + var observable = invocationContexts.Select(i => i.KernelEvents).Merge(); + + foreach (var invocation in invocationContexts) + { + await invocation.InvokeAsync(); + } + + return new KernelCommandResult(observable); + } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/ICodeSubmissionPreProcessor.cs b/WorkspaceServer/Kernel/KernelPipelineContinuation.cs similarity index 57% rename from WorkspaceServer/Kernel/ICodeSubmissionPreProcessor.cs rename to WorkspaceServer/Kernel/KernelPipelineContinuation.cs index bb7b1e4f1..6da3787bd 100644 --- a/WorkspaceServer/Kernel/ICodeSubmissionPreProcessor.cs +++ b/WorkspaceServer/Kernel/KernelPipelineContinuation.cs @@ -1,14 +1,11 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.CommandLine; using System.Threading.Tasks; namespace WorkspaceServer.Kernel { - public interface ICodeSubmissionProcessor - { - Task ProcessAsync(SubmitCode codeSubmission); - Command Command { get; } - } -} + public delegate Task KernelPipelineContinuation( + IKernelCommand command, + KernelPipelineContext context); +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/PackageAdded.cs b/WorkspaceServer/Kernel/NoSuitableKernelException.cs similarity index 52% rename from WorkspaceServer/Kernel/PackageAdded.cs rename to WorkspaceServer/Kernel/NoSuitableKernelException.cs index ec53aefb7..5cd075892 100644 --- a/WorkspaceServer/Kernel/PackageAdded.cs +++ b/WorkspaceServer/Kernel/NoSuitableKernelException.cs @@ -5,14 +5,7 @@ namespace WorkspaceServer.Kernel { - public class PackageAdded : KernelEventBase + public class NoSuitableKernelException : Exception { - public PackageAdded(IKernelCommand command) : this(command.Id) - { - } - - public PackageAdded(Guid parentId) : base(parentId) - { - } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/NuGetPackageAdded.cs b/WorkspaceServer/Kernel/NuGetPackageAdded.cs new file mode 100644 index 000000000..1b2dea7f0 --- /dev/null +++ b/WorkspaceServer/Kernel/NuGetPackageAdded.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public class NuGetPackageAdded : KernelEventBase + { + public NuGetPackageAdded(NugetPackageReference packageReference) + { + PackageReference = packageReference; + } + + public NugetPackageReference PackageReference { get; } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/NugetPackageReference.cs b/WorkspaceServer/Kernel/NugetPackageReference.cs new file mode 100644 index 000000000..5bcd8c8d4 --- /dev/null +++ b/WorkspaceServer/Kernel/NugetPackageReference.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Text.RegularExpressions; + +namespace WorkspaceServer.Kernel +{ + public class NugetPackageReference + { + private static Regex _regex = new Regex( + @"nuget:\s*(?[\w\.]+)(\s*,\s*(?[\w\.\-]+))?", + RegexOptions.Compiled | + RegexOptions.CultureInvariant | + RegexOptions.Singleline); + + public NugetPackageReference(string packageName, string packageVersion = null) + { + if (string.IsNullOrWhiteSpace(packageName)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageName)); + } + + PackageName = packageName; + PackageVersion = packageVersion ?? string.Empty; + } + + public string PackageName { get; } + + public string PackageVersion { get; } + + public static bool TryParse(string value, out NugetPackageReference reference) + { + var result = _regex.Match(value); + + if (!result.Success) + { + reference = null; + return false; + } + + var packageName = result.Groups["packageName"].Value; + var packageVersion = result.Groups["packageVersion"].Value; + + reference = new NugetPackageReference(packageName, packageVersion); + + return true; + } + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/RequestCompletion.cs b/WorkspaceServer/Kernel/RequestCompletion.cs new file mode 100644 index 000000000..98ec176e9 --- /dev/null +++ b/WorkspaceServer/Kernel/RequestCompletion.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public class RequestCompletion : KernelCommandBase + { + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/RequestDiagnostics.cs b/WorkspaceServer/Kernel/RequestDiagnostics.cs new file mode 100644 index 000000000..705a4f6a8 --- /dev/null +++ b/WorkspaceServer/Kernel/RequestDiagnostics.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public class RequestDiagnostics : KernelCommandBase + { + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/RequestDocumentation.cs b/WorkspaceServer/Kernel/RequestDocumentation.cs new file mode 100644 index 000000000..aa055c1c9 --- /dev/null +++ b/WorkspaceServer/Kernel/RequestDocumentation.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public class RequestDocumentation : KernelCommandBase + { + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/RequestSignatureHelp.cs b/WorkspaceServer/Kernel/RequestSignatureHelp.cs new file mode 100644 index 000000000..292beb6af --- /dev/null +++ b/WorkspaceServer/Kernel/RequestSignatureHelp.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public class RequestSignatureHelp : KernelCommandBase + { + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/SendStandardInput.cs b/WorkspaceServer/Kernel/SendStandardInput.cs new file mode 100644 index 000000000..fc3d58917 --- /dev/null +++ b/WorkspaceServer/Kernel/SendStandardInput.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace WorkspaceServer.Kernel +{ + public class SendStandardInput : KernelCommandBase + { + } +} \ No newline at end of file diff --git a/WorkspaceServer/Kernel/SignatureHelpReceived.cs b/WorkspaceServer/Kernel/SignatureHelpReceived.cs index 768f0f0ff..9b05042e3 100644 --- a/WorkspaceServer/Kernel/SignatureHelpReceived.cs +++ b/WorkspaceServer/Kernel/SignatureHelpReceived.cs @@ -7,12 +7,9 @@ namespace WorkspaceServer.Kernel { public class SignatureHelpReceived : KernelEventBase { - public SignatureHelpReceived(IKernelCommand command) : this(command.Id) + public SignatureHelpReceived(IKernelCommand command) : base(command) { } - public SignatureHelpReceived(Guid parentId) : base(parentId) - { - } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/StandardErrorReceived.cs b/WorkspaceServer/Kernel/StandardErrorReceived.cs index a07ef3be8..4e60939c2 100644 --- a/WorkspaceServer/Kernel/StandardErrorReceived.cs +++ b/WorkspaceServer/Kernel/StandardErrorReceived.cs @@ -1,17 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class StandardErrorReceived : KernelEventBase { - public string Content { get; } - - public StandardErrorReceived( string content) : base(Guid.NewGuid(), Guid.Empty) + public StandardErrorReceived(string content) { - Content = content; + Content = content ?? string.Empty; } + + public string Content { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/StandardInputReceived.cs b/WorkspaceServer/Kernel/StandardInputReceived.cs index 3e7ce8fec..3b22a7870 100644 --- a/WorkspaceServer/Kernel/StandardInputReceived.cs +++ b/WorkspaceServer/Kernel/StandardInputReceived.cs @@ -1,21 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class StandardInputReceived : KernelEventBase { - public string Content { get; } - - public StandardInputReceived(IKernelCommand command, string content) : this(command.Id, content) + public StandardInputReceived(string content) { + Content = content ?? string.Empty; } - public StandardInputReceived(Guid parentId, string content) : base(parentId) - { - Content = content; - } + public string Content { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/StandardOutputReceived.cs b/WorkspaceServer/Kernel/StandardOutputReceived.cs index 2cf7cf1a2..d1474cdcd 100644 --- a/WorkspaceServer/Kernel/StandardOutputReceived.cs +++ b/WorkspaceServer/Kernel/StandardOutputReceived.cs @@ -1,17 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class StandardOutputReceived : KernelEventBase { - public string Content { get; } - - public StandardOutputReceived(string content) : base(Guid.NewGuid(), Guid.Empty) + public StandardOutputReceived(string content) { - Content = content; + Content = content ?? string.Empty; } + + public string Content { get; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/Started.cs b/WorkspaceServer/Kernel/Started.cs index ed0cc894c..edb949e33 100644 --- a/WorkspaceServer/Kernel/Started.cs +++ b/WorkspaceServer/Kernel/Started.cs @@ -1,14 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class Started : KernelEventBase { - public Started():base(Guid.NewGuid(), Guid.Empty) - { - } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/Stopped.cs b/WorkspaceServer/Kernel/Stopped.cs index 5e9b12175..821b34fb2 100644 --- a/WorkspaceServer/Kernel/Stopped.cs +++ b/WorkspaceServer/Kernel/Stopped.cs @@ -1,14 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class Stopped : KernelEventBase { - public Stopped() : base(Guid.NewGuid(), Guid.Empty) - { - } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/SubmitCode.cs b/WorkspaceServer/Kernel/SubmitCode.cs index 186d219e8..2dc88a2d8 100644 --- a/WorkspaceServer/Kernel/SubmitCode.cs +++ b/WorkspaceServer/Kernel/SubmitCode.cs @@ -7,13 +7,16 @@ namespace WorkspaceServer.Kernel { public class SubmitCode : KernelCommandBase { - public string Code { get; set; } - public string Language { get; set; } - - public SubmitCode(string code, string language = null) + public SubmitCode( + string code, + string targetKernelName = null) { Code = code ?? throw new ArgumentNullException(nameof(code)); - Language = language; + TargetKernelName = targetKernelName; } + + public string Code { get; set; } + + public string TargetKernelName { get; set; } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/ValueProduced.cs b/WorkspaceServer/Kernel/ValueProduced.cs index 0ebb5f3ef..aeff6ae05 100644 --- a/WorkspaceServer/Kernel/ValueProduced.cs +++ b/WorkspaceServer/Kernel/ValueProduced.cs @@ -1,17 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace WorkspaceServer.Kernel { public class ValueProduced: KernelEventBase { - public object Value { get; } - - public ValueProduced(Guid parentId, object value) : base(parentId) + public ValueProduced(object value, SubmitCode submitCode) : base(submitCode) { Value = value; } + + public object Value { get; } } } \ No newline at end of file