diff --git a/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs b/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs index 042776c5e..5afd27067 100644 --- a/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs +++ b/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs @@ -109,7 +109,7 @@ public async Task it_returns_diagnostics() } [Fact] - public async Task it_notifies_when_submission_is_complete() + public async Task it_cannot_execute_incomplete_submissions() { var kernel = CreateKernel(); @@ -122,27 +122,9 @@ public async Task it_notifies_when_submission_is_complete() KernelEvents .Should() - .Contain(e => e.Value is CodeSubmissionEvaluated); - } - - [Fact] - public async Task it_notifies_when_submission_is_incomplete() - { - var kernel = CreateKernel(); - - await kernel.SendAsync(new SubmitCode("var a =")); - - KernelEvents - .Should() - .NotContain(e => e.Value is ValueProduced); - - KernelEvents - .ValuesOnly() - .Should() - .Contain(e => e is CodeSubmissionEvaluated) - .And - .Contain(e => e is IncompleteCodeSubmissionReceived); + .Contain(e => e.Value is CodeSubmissionEvaluationFailed); } + [Fact] public async Task expression_evaluated_to_null_has_result_with_null_value() diff --git a/WorkspaceServer/Kernel/CSharpKernel.cs b/WorkspaceServer/Kernel/CSharpKernel.cs index 7182d72bf..9f9872cb0 100644 --- a/WorkspaceServer/Kernel/CSharpKernel.cs +++ b/WorkspaceServer/Kernel/CSharpKernel.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Scripting; @@ -34,11 +33,7 @@ public class CSharpKernel : KernelBase .GetMethod("HasReturnValue", BindingFlags.Instance | BindingFlags.NonPublic); private ScriptState _scriptState; - - protected CSharpParseOptions ParseOptions = new CSharpParseOptions(LanguageVersion.Default, kind: SourceCodeKind.Script); protected ScriptOptions ScriptOptions; - - private StringBuilder _inputBuffer = new StringBuilder(); private ImmutableArray _metadataReferences; private WorkspaceFixture _fixture; @@ -64,28 +59,12 @@ private void SetupScriptOptions() typeof(Enumerable).Assembly, typeof(IEnumerable<>).Assembly, typeof(Task<>).Assembly, - typeof(IKernel).Assembly, - typeof(CSharpKernel).Assembly, + typeof(IKernel).Assembly, + typeof(CSharpKernel).Assembly, typeof(PocketView).Assembly, typeof(XPlot.Plotly.PlotlyChart).Assembly); } - 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 override async Task HandleAsync( IKernelCommand command, KernelInvocationContext context) @@ -115,83 +94,75 @@ private async Task HandleSubmitCode( var codeSubmissionReceived = new CodeSubmissionReceived( submitCode.Code, submitCode); + context.OnNext(codeSubmissionReceived); - var (shouldExecute, code) = IsBufferACompleteSubmission(submitCode.Code); + var code = submitCode.Code; - if (shouldExecute) - { - context.OnNext(new CompleteCodeSubmissionReceived(submitCode)); - Exception exception = null; + context.OnNext(new CompleteCodeSubmissionReceived(submitCode)); + Exception exception = null; - using var console = await ConsoleOutput.Capture(); - using var _ = console.SubscribeToStandardOutput(std => PublishOutput(std, context, submitCode)); + using var console = await ConsoleOutput.Capture(); + using var _ = console.SubscribeToStandardOutput(std => PublishOutput(std, context, submitCode)); - try + try + { + if (_scriptState == null) { - if (_scriptState == null) - { - _scriptState = await CSharpScript.RunAsync( - code, - ScriptOptions); - } - else - { - _scriptState = await _scriptState.ContinueWithAsync( - code, - ScriptOptions, - e => - { - exception = e; - return true; - }); - } + _scriptState = await CSharpScript.RunAsync( + code, + ScriptOptions); } - catch (Exception e) + else { - exception = e; + _scriptState = await _scriptState.ContinueWithAsync( + code, + ScriptOptions, + e => + { + exception = e; + return true; + }); } + } + catch (Exception e) + { + exception = e; + } - if (exception != null) - { - var message = string.Join("\n", (_scriptState?.Script?.GetDiagnostics() ?? - Enumerable.Empty()).Select(d => d.GetMessage())); + if (exception != null) + { + var message = string.Join("\n", (_scriptState?.Script?.GetDiagnostics() ?? + Enumerable.Empty()).Select(d => d.GetMessage())); - context.OnNext(new CodeSubmissionEvaluationFailed(exception, message, submitCode)); - context.OnError(exception); - } - else + context.OnNext(new CodeSubmissionEvaluationFailed(exception, message, submitCode)); + context.OnError(exception); + } + else + { + if (HasReturnValue) { - if (HasReturnValue) - { - var returnValueType = _scriptState.ReturnValue?.GetType(); + var returnValueType = _scriptState.ReturnValue?.GetType(); - var mimeType = MimeTypeFor(returnValueType); + var mimeType = MimeTypeFor(returnValueType); - var formatted = _scriptState.ReturnValue.ToDisplayString(mimeType); + var formatted = _scriptState.ReturnValue.ToDisplayString(mimeType); - var formattedValues = new List + var formattedValues = new List { new FormattedValue(mimeType, formatted) }; - context.OnNext( - new ValueProduced( - _scriptState.ReturnValue, - submitCode, - true, - formattedValues)); - } - - context.OnNext(new CodeSubmissionEvaluated(submitCode)); - - context.OnCompleted(); + context.OnNext( + new ValueProduced( + _scriptState.ReturnValue, + submitCode, + true, + formattedValues)); } - } - else - { - context.OnNext(new IncompleteCodeSubmissionReceived(submitCode)); + context.OnNext(new CodeSubmissionEvaluated(submitCode)); + context.OnCompleted(); } } @@ -205,8 +176,8 @@ private static string MimeTypeFor(Type returnValueType) } private void PublishOutput( - string output, - KernelInvocationContext context, + string output, + KernelInvocationContext context, IKernelCommand command) { var formattedValues = new List @@ -251,14 +222,20 @@ private async Task> GetCompletionList(string code, i var forcedState = false; if (scriptState == null) { - scriptState = await CSharpScript.RunAsync("", ScriptOptions); + scriptState = await CSharpScript.RunAsync(string.Empty, ScriptOptions); forcedState = true; } var compilation = scriptState.Script.GetCompilation(); metadataReferences = metadataReferences.AddRange(compilation.References); + var originalCode = forcedState ? string.Empty : scriptState.Script.Code ?? string.Empty; + + var buffer = new StringBuilder(originalCode); + if (!string.IsNullOrWhiteSpace(originalCode) && !originalCode.EndsWith(Environment.NewLine)) + { + buffer.AppendLine(); + } - var buffer = new StringBuilder(forcedState ? string.Empty : scriptState.Script.Code ?? string.Empty); buffer.AppendLine(code); var fullScriptCode = buffer.ToString(); var offset = fullScriptCode.LastIndexOf(code, StringComparison.InvariantCulture);