这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions Microsoft.DotNet.Interactive.Jupyter/ExecuteRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,9 @@ public async Task Handle(JupyterRequestContext context)
}
}

private static Dictionary<string, object> CreateTransient()
private static Dictionary<string, object> CreateTransient(string displayId)
{
var id = Guid.NewGuid();
var transient = new Dictionary<string, object> { { "display_id", id.ToString() } };
var transient = new Dictionary<string, object> { { "display_id", displayId ?? Guid.NewGuid().ToString() } };
return transient;
}

Expand Down Expand Up @@ -155,18 +154,23 @@ private void OnValueProduced(ValueProduced valueProduced)

try
{
var transient = CreateTransient();
// executeResult data
var executeResultData = valueProduced.IsLastValue
var transient = CreateTransient(valueProduced.ValueId);

var executeResultData = valueProduced.IsReturnValue
? new ExecuteResult(
openRequest.ExecutionCount,
transient: transient,
data: valueProduced?.FormattedValues
?.ToDictionary(k => k.MimeType, v => v.Value))
: new DisplayData(
transient: transient,
data: valueProduced?.FormattedValues
?.ToDictionary(k => k.MimeType, v => v.Value));
: valueProduced.IsUpdatedValue
? new UpdateDisplayData(
transient: transient,
data: valueProduced?.FormattedValues
?.ToDictionary(k => k.MimeType, v => v.Value))
: new DisplayData(
transient: transient,
data: valueProduced?.FormattedValues
?.ToDictionary(k => k.MimeType, v => v.Value));

if (!openRequest.Request.Silent)
{
Expand Down
5 changes: 4 additions & 1 deletion Microsoft.DotNet.Interactive/Commands/DisplayValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ namespace Microsoft.DotNet.Interactive.Commands
{
public class DisplayValue : KernelCommandBase
{
public DisplayValue(FormattedValue formattedValue)
public DisplayValue(FormattedValue formattedValue, string valueId = null)
{
FormattedValue = formattedValue;
ValueId = valueId;
}

public FormattedValue FormattedValue { get; }

public string ValueId { get; }
}
}
23 changes: 23 additions & 0 deletions Microsoft.DotNet.Interactive/Commands/UpdateDisplayedValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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 Microsoft.DotNet.Interactive.Commands
{
public class UpdateDisplayedValue : KernelCommandBase
{
public UpdateDisplayedValue(FormattedValue formattedValue, string valueId)
{
if (string.IsNullOrWhiteSpace(valueId))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(valueId));
}
FormattedValue = formattedValue;
ValueId = valueId;
}

public FormattedValue FormattedValue { get; }
public string ValueId { get; }
}
}
44 changes: 44 additions & 0 deletions Microsoft.DotNet.Interactive/DisplayedValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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 Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Rendering;

namespace Microsoft.DotNet.Interactive
{
public class DisplayedValue
{
private readonly string _displayId;
private readonly string _mimeType;

public DisplayedValue(string displayId, string mimeType)
{
if (string.IsNullOrWhiteSpace(displayId))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(displayId));
}

if (string.IsNullOrWhiteSpace(mimeType))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(mimeType));
}
_displayId = displayId;
_mimeType = mimeType;
}

public void Update(object updatedValue)
{
var formatted = new FormattedValue(
_mimeType,
updatedValue.ToDisplayString(_mimeType));

var kernel = KernelInvocationContext.Current.Kernel;

Task.Run(() =>
kernel.SendAsync(new UpdateDisplayedValue(formatted, _displayId)))
.Wait();
}
}
}
26 changes: 22 additions & 4 deletions Microsoft.DotNet.Interactive/Events/ValueProduced.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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 Microsoft.DotNet.Interactive.Commands;

Expand All @@ -10,18 +11,35 @@ public class ValueProduced : KernelEventBase
{
public ValueProduced(object value,
IKernelCommand command,
bool isLastValue = false,
IReadOnlyCollection<FormattedValue> formattedValues = null) : base(command)
bool isReturnValue = false,
IReadOnlyCollection<FormattedValue> formattedValues = null,
string valueId= null,
bool isUpdatedValue = false) : base(command)
{
if (isUpdatedValue && valueId == null)
{
throw new ArgumentException($"{nameof(isUpdatedValue)} cannot be true with a null {nameof(valueId)}", nameof(valueId));
}

Value = value;
IsLastValue = isLastValue;
IsReturnValue = isReturnValue;
FormattedValues = formattedValues;
ValueId = valueId;
IsUpdatedValue = valueId switch
{
null => false,
_ => isUpdatedValue
};
}

public object Value { get; }

public bool IsLastValue { get; }
public bool IsReturnValue { get; }

public IReadOnlyCollection<FormattedValue> FormattedValues { get; }

public string ValueId { get; }

public bool IsUpdatedValue { get; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this should implicitly be true if an id is set. Would it not be a coding error if this is set to true while id is set to null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is false if id is not set, but can be false even if Id is set

}
}
9 changes: 7 additions & 2 deletions Microsoft.DotNet.Interactive/Kernel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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 Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Rendering;
Expand All @@ -10,21 +11,25 @@ namespace Microsoft.DotNet.Interactive
{
public static class Kernel
{
public static void display(
public static DisplayedValue display(
object value,
string mimeType = HtmlFormatter.MimeType)
{
var displayId = Guid.NewGuid().ToString();
var formatted = new FormattedValue(
mimeType,
value.ToDisplayString(mimeType));

var kernel = KernelInvocationContext.Current.Kernel;

Task.Run(() =>
kernel.SendAsync(new DisplayValue(formatted)))
kernel.SendAsync(new DisplayValue(formatted, displayId)))
.Wait();
return new DisplayedValue(displayId, mimeType);
}



public static void Javascript(
string scriptContent)
{
Expand Down
37 changes: 27 additions & 10 deletions Microsoft.DotNet.Interactive/KernelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ private void AddDirectiveMiddleware()
context,
next),

_ => next(command, context)
UpdateDisplayedValue updateDisplayValue =>
HandleUpdateDisplayValue(
updateDisplayValue,
context,
next),

_ => next(command, context)
});
}

Expand Down Expand Up @@ -161,33 +167,44 @@ private async Task HandleDisplayValue(
KernelInvocationContext pipelineContext,
KernelPipelineContinuation next)
{
displayValue.Handler = context =>
displayValue.Handler = invocationContext =>
{
context.OnNext(
invocationContext.OnNext(
new ValueProduced(
displayValue.FormattedValue,
displayValue,
formattedValues: new[] { displayValue.FormattedValue }));
formattedValues: new[] { displayValue.FormattedValue },
valueId: displayValue.ValueId));

context.OnCompleted();
invocationContext.OnCompleted();

return Task.CompletedTask;
};

displayValue.Handler = invocationContext =>
await next(displayValue, pipelineContext);
}

private async Task HandleUpdateDisplayValue(
UpdateDisplayedValue displayedValue,
KernelInvocationContext pipelineContext,
KernelPipelineContinuation next)
{
displayedValue.Handler = invocationContext =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this just over-write the Handler set in the line above this?

{
invocationContext.OnNext(
new ValueProduced(
displayValue.FormattedValue,
displayValue,
formattedValues: new[] { displayValue.FormattedValue }));
displayedValue.FormattedValue,
displayedValue,
formattedValues: new[] { displayedValue.FormattedValue },
valueId: displayedValue.ValueId,
isUpdatedValue: true));

invocationContext.OnCompleted();

return Task.CompletedTask;
};

await next(displayValue, pipelineContext);
await next(displayedValue, pipelineContext);
}

private Parser BuildDirectiveParser()
Expand Down
52 changes: 52 additions & 0 deletions WorkspaceServer.Tests/Kernel/CSharpKernelRenderingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,58 @@ public async Task Display_helper_can_be_called_without_specifying_class_name()
v.Value.ToString().Contains("<b>hi!</b>"));
}

[Fact]
public async Task Displayed_value_can_be_updated()
{
var kernel = CreateKernel();

await kernel.SendAsync(new SubmitCode("var d = display(b(\"hello\")); d.Update(b(\"world\"));"));

var formatted =
KernelEvents
.OrderBy(e => e.Timestamp)
.ValuesOnly()
.OfType<ValueProduced>()
.SelectMany(v => v.FormattedValues);

formatted
.Should()
.ContainSingle(v =>
v.MimeType == "text/html" &&
v.Value.ToString().Contains("<b>hello</b>"))
.And
.ContainSingle(v =>
v.MimeType == "text/html" &&
v.Value.ToString().Contains("<b>world</b>"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion doesn't check that they're also in the correct order.

}

[Fact]
public async Task Value_display_and_update_are_in_right_order()
{
var kernel = CreateKernel();

await kernel.SendAsync(new SubmitCode("var d = display(b(\"hello\")); d.Update(b(\"world\"));"));

var formatted =
KernelEvents
.OrderBy(e => e.Timestamp)
.ValuesOnly()
.OfType<ValueProduced>()
.SelectMany(v => v.FormattedValues).ToList();

var firstValue = formatted.Select((v, i) => new {v, i}).First(e => e.v.MimeType == "text/html" &&
e.v.Value.ToString()
.Contains("<b>hello</b>")).i;

var updatedValue = formatted.Select((v, i) => new { v, i }).First(e => e.v.MimeType == "text/html" &&
e.v.Value.ToString()
.Contains("<b>world</b>")).i;

updatedValue
.Should()
.BeGreaterOrEqualTo(firstValue);
}

[Fact]
public async Task Javascript_helper_emits_string_as_content_within_a_script_element()
{
Expand Down
2 changes: 1 addition & 1 deletion WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public async Task it_produces_a_final_value_if_the_code_expression_evaluates()
.Should()
.HaveCount(4)
.And
.ContainSingle(e => e.IsLastValue);
.ContainSingle(e => e.IsReturnValue);

}

Expand Down