这是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
2 changes: 1 addition & 1 deletion FSharpWorkspaceShim/FSharpWorkspaceShim.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.6.2" />
<PackageReference Update="FSharp.Core" Version="4.6.3-beta.19425.1" />
<PackageReference Include="FSharp.Compiler.Service" Version="28.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="3.1.0-beta3-final" />
</ItemGroup>
Expand Down
71 changes: 18 additions & 53 deletions Microsoft.DotNet.Interactive.FSharp/FSharpKernel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,35 @@

namespace Microsoft.DotNet.Interactive.FSharp

open System
open System.ComponentModel
open System.Diagnostics
open System.Text
open System.Threading.Tasks
open FSharp.Compiler.Scripting
open Microsoft.DotNet.Interactive
open Microsoft.DotNet.Interactive.Commands
open Microsoft.DotNet.Interactive.Events
open MLS.Agent.Tools

type FSharpKernel() =
inherit KernelBase()
let mutable proc: Process = null
let write (s: string) = proc.StandardInput.Write(s)
let sentinelValue = Guid.NewGuid().ToString()
let sentinelFound = Event<_>()
let stdout = StringBuilder()
let waitForReady () =
async {
write <| sprintf ";;printfn \"\\n%s\";;\n" sentinelValue
do! Async.AwaitEvent sentinelFound.Publish
}
let startProcess () =
async {
if isNull proc then
let outputReceived line =
if line = sentinelValue then
sentinelFound.Trigger()
else
stdout.AppendLine(line) |> ignore
proc <- Dotnet().StartProcess("fsi --nologo", output = Action<string>(outputReceived))
do! waitForReady()
}
let eval (code: string) =
async {
do! startProcess ()
stdout.Clear() |> ignore
write code
do! waitForReady ()
let value = stdout.ToString()
// trim garbage
let nl = Environment.NewLine
let headerGarbage = sprintf "val it : unit = ()%s%s" nl nl
let value = if value.StartsWith(headerGarbage) then value.Substring(headerGarbage.Length) else value
let footerGarbage = sprintf "%s%s> %s" nl nl nl
let value = if value.EndsWith(footerGarbage) then value.Substring(0, value.Length - footerGarbage.Length) else value
return value
}
do base.AddDisposable({ new IDisposable with
member __.Dispose() =
if not <| isNull proc then
try
proc.Kill()
with
| :? InvalidOperationException -> ()
| :? NotSupportedException -> ()
| :? Win32Exception -> () })
let script = new FSharpScript()
do base.AddDisposable(script)
let handleSubmitCode (codeSubmission: SubmitCode) (context: KernelInvocationContext) =
async {
let codeSubmissionReceived = CodeSubmissionReceived(codeSubmission.Code, codeSubmission)
context.OnNext(codeSubmissionReceived)
// submit code
let! value = eval codeSubmission.Code
context.OnNext(ValueProduced(value, codeSubmission, true, [FormattedValue("text/plain", value)]))
let result, errors =
try
script.Eval(codeSubmission.Code)
with
| ex -> Error(ex), [||]
if errors.Length > 0 then
let aggregateErrorMessage = System.String.Join("\n", errors)
context.OnNext(CodeSubmissionEvaluationFailed(aggregateErrorMessage, codeSubmission))
match result with
| Ok(Some(value)) ->
let value = value.ReflectionValue
let formattedValues = FormattedValue.FromObject(value)
context.OnNext(ValueProduced(value, codeSubmission, true, formattedValues))
| Ok(None) -> ()
| Error(ex) -> context.OnError(ex)
context.OnNext(CodeSubmissionEvaluated(codeSubmission))
context.OnCompleted()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@

<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.Interactive\Microsoft.DotNet.Interactive.csproj" />
<ProjectReference Include="..\Microsoft.DotNet.Interactive.Rendering\Microsoft.DotNet.Interactive.Rendering.csproj" />
<ProjectReference Include="..\MLS.Agent.Tools\MLS.Agent.Tools.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FSharp.Compiler.Private.Scripting" Version="10.5.0-beta.19425.1" />
<PackageReference Include="FSharp.Core" Version="4.6.3-beta.19425.1" />
</ItemGroup>

</Project>
23 changes: 12 additions & 11 deletions Microsoft.DotNet.Interactive.Jupyter/ExecuteRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,19 +208,20 @@ private void OnValueProduced(ValueProduced valueProduced)

private void OnCodeSubmissionEvaluated(CodeSubmissionEvaluated codeSubmissionEvaluated)
{
InFlightRequests.TryRemove(codeSubmissionEvaluated.Command, out var openRequest);

// reply ok
var executeReplyPayload = new ExecuteReplyOk(executionCount: openRequest.ExecutionCount);
if (InFlightRequests.TryRemove(codeSubmissionEvaluated.Command, out var openRequest))
Copy link

Choose a reason for hiding this comment

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

This feels like it's working around a bug? What's the case where we do multiple evaluations of the same command?

Copy link
Contributor

Choose a reason for hiding this comment

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

I hit this as well. There can be multiple replies to a given a command and if one arrives after the command is marked as completed, there was a NullReferenceException here.

{
// reply ok
var executeReplyPayload = new ExecuteReplyOk(executionCount: openRequest.ExecutionCount);

// send to server
var executeReply = Message.CreateResponse(
executeReplyPayload,
openRequest.Context.Request);
// send to server
var executeReply = Message.CreateResponse(
executeReplyPayload,
openRequest.Context.Request);

openRequest.Context.ServerChannel.Send(executeReply);
openRequest.Context.RequestHandlerStatus.SetAsIdle();
openRequest.Dispose();
openRequest.Context.ServerChannel.Send(executeReply);
openRequest.Context.RequestHandlerStatus.SetAsIdle();
openRequest.Dispose();
}
}
}
}
6 changes: 3 additions & 3 deletions Microsoft.DotNet.Interactive.Rendering/Formatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public static void ResetToDefault()
}

public static string ToDisplayString(
this object obj,
this object obj,
string mimeType = Rendering.PlainTextFormatter.MimeType)
{
// TODO: (ToDisplayString) rename
Expand Down Expand Up @@ -143,7 +143,7 @@ public static string ToDisplayString(
}

public static void FormatTo<T>(
this T obj,
this T obj,
TextWriter writer,
string mimeType = Rendering.PlainTextFormatter.MimeType)
{
Expand Down Expand Up @@ -181,7 +181,7 @@ internal static Action<object, TextWriter, string> GetGenericFormatterMethod(thi
var writerParam = Expression.Parameter(typeof(TextWriter), "target");
var mimeTypeParam = Expression.Parameter(typeof(string), "target");

var methodCallExpr = Expression.Call(null,
var methodCallExpr = Expression.Call(null,
methodInfo,
Expression.Convert(targetParam, type),
writerParam,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public CodeSubmissionEvaluationFailed(
: message;
}

public CodeSubmissionEvaluationFailed(
string message,
SubmitCode submitCode) : this(null, message, submitCode)
{
}

public string Code => ((SubmitCode)Command).Code;

public Exception Exception { get; }
Expand Down
24 changes: 24 additions & 0 deletions Microsoft.DotNet.Interactive/FormattedValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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.Rendering;

namespace Microsoft.DotNet.Interactive
{
Expand All @@ -21,5 +23,27 @@ public FormattedValue(string mimeType, object value)
public string MimeType { get; }

public object Value { get; }

public static IReadOnlyCollection<FormattedValue> FromObject(object value)
{
var type = value?.GetType();

var mimeType = MimeTypeFor(type);

var formatted = value.ToDisplayString(mimeType);

return new FormattedValue[]
{
new FormattedValue(mimeType, formatted)
};
}

private static string MimeTypeFor(Type returnValueType)
{
return returnValueType?.IsPrimitive == true ||
returnValueType == typeof(string)
? "text/plain"
: "text/html";
}
}
}
4 changes: 3 additions & 1 deletion Microsoft.DotNet.Interactive/KernelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ private async Task HandleDirectivesAndSubmitCode(

var parseResult = BuildDirectiveParser().Parse(currentLine);

if (parseResult.Errors.Count == 0)
if (parseResult.Errors.Count == 0 &&
!parseResult.Directives.Any() && // System.CommandLine directives should not be considered as valid
!parseResult.Tokens.Any(t => t.Type == TokenType.Directive))
{
modified = true;
await _directiveParser.InvokeAsync(parseResult);
Expand Down
1 change: 1 addition & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
<add key="dotnet-fsharp" value="https://dotnet.myget.org/F/fsharp/api/v3/index.json" />
<add key="dotnet-try" value="https://dotnet.myget.org/F/dotnet-try/api/v3/index.json" />
</packageSources>
</configuration>
18 changes: 14 additions & 4 deletions WorkspaceServer.Tests/Kernel/FSharpKernelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,30 @@ public async Task it_returns_an_object_value()
{
var kernel = CreateKernel();
await kernel.SendAsync(new SubmitCode("123"));
AssertLastValue("> val it : int = 123");
AssertLastValue(123);
}

[Fact]
public async Task it_remembers_state_between_submissions()
{
var kernel = CreateKernel();
await kernel.SendAsync(new SubmitCode("let add x y = x + y"));
AssertLastValue("> val add : x:int -> y:int -> int");
await kernel.SendAsync(new SubmitCode("add 2 3"));
AssertLastValue("> val it : int = 5");
AssertLastValue(5);
}

private void AssertLastValue(string value)
[Fact]
public async Task kernel_base_ignores_command_line_directives()
{
// The text `[1;2;3;4]` parses as a System.CommandLine directive; ensure it's not consumed and is passed on to the kernel.
var kernel = CreateKernel();
await kernel.SendAsync(new SubmitCode(@"
[1;2;3;4]
|> List.sum"));
AssertLastValue(10);
}

private void AssertLastValue(object value)
{
KernelEvents.ValuesOnly()
.OfType<ValueProduced>()
Expand Down
20 changes: 1 addition & 19 deletions WorkspaceServer/Kernel/CSharpKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,7 @@ private async Task HandleSubmitCode(
{
if (HasReturnValue)
{
var returnValueType = _scriptState.ReturnValue?.GetType();

var mimeType = MimeTypeFor(returnValueType);

var formatted = _scriptState.ReturnValue.ToDisplayString(mimeType);

var formattedValues = new List<FormattedValue>
{
new FormattedValue(mimeType, formatted)
};

var formattedValues = FormattedValue.FromObject(_scriptState.ReturnValue);
context.OnNext(
new ValueProduced(
_scriptState.ReturnValue,
Expand All @@ -167,14 +157,6 @@ private async Task HandleSubmitCode(
}
}

private static string MimeTypeFor(Type returnValueType)
{
return returnValueType?.IsPrimitive == true ||
returnValueType == typeof(string)
? "text/plain"
: "text/html";
}

private void PublishOutput(
string output,
KernelInvocationContext context,
Expand Down
1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
$(RestoreSources);
https://dotnet.myget.org/F/dotnet-try/api/v3/index.json;
https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json;
https://dotnet.myget.org/F/fsharp/api/v3/index.json
</RestoreSources>
</PropertyGroup>
</Project>