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
42 changes: 42 additions & 0 deletions Microsoft.DotNet.Interactive.CSharp.Tests/CSharpKernelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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.Tasks;
using FluentAssertions;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Tests;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.DotNet.Interactive.CSharp.Tests
{
public class CSharpKernelTests : LanguageKernelTestBase
{
public CSharpKernelTests(ITestOutputHelper output) : base(output)
{
}

[Fact]
public async Task Script_state_is_available_within_middleware_pipeline()
{
var variableCountBeforeEvaluation = 0;
var variableCountAfterEvaluation = 0;

using var kernel = new CSharpKernel();

kernel.AddMiddleware(async (command, context, next) =>
{
var k = context.HandlingKernel as CSharpKernel;

await next(command, context);

variableCountAfterEvaluation = k.ScriptState.Variables.Length;
});

await kernel.SendAsync(new SubmitCode("var x = 1;"));

variableCountBeforeEvaluation.Should().Be(0);
variableCountAfterEvaluation.Should().Be(1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Tests;
using Microsoft.DotNet.Interactive.Tests.Utility;
using Xunit;

Expand Down
40 changes: 40 additions & 0 deletions Microsoft.DotNet.Interactive.CSharp/CSharpCodeGeneration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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.IO;

namespace Microsoft.DotNet.Interactive.CSharp
{
internal static partial class TypeExtensions
{
public static void WriteCSharpDeclarationTo(
this Type type,
TextWriter writer)
{
var typeName = type.FullName ?? type.Name;

if (typeName.Contains("`"))
{
writer.Write(typeName.Remove(typeName.IndexOf('`')));
writer.Write("<");
var genericArguments = type.GetGenericArguments();

for (var i = 0; i < genericArguments.Length; i++)
{
genericArguments[i].WriteCSharpDeclarationTo(writer);
if (i < genericArguments.Length - 1)
{
writer.Write(",");
}
}

writer.Write(">");
}
else
{
writer.Write(typeName);
}
}
}
}
119 changes: 70 additions & 49 deletions Microsoft.DotNet.Interactive.CSharp/CSharpKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Interactive.DependencyManager;
using Microsoft.CodeAnalysis;
Expand All @@ -25,12 +25,11 @@
using Newtonsoft.Json.Linq;
using XPlot.Plotly;
using Task = System.Threading.Tasks.Task;
using System.ComponentModel;

namespace Microsoft.DotNet.Interactive.CSharp
{
public class CSharpKernel :
KernelBase,
DotNetLanguageKernel,
IExtensibleKernel,
ISupportNuget
{
Expand All @@ -43,9 +42,9 @@ public class CSharpKernel :
new CSharpParseOptions(LanguageVersion.Default, kind: SourceCodeKind.Script);

private WorkspaceFixture _fixture;

private AssemblyResolutionProbe _assemblyProbingPaths;
private NativeResolutionProbe _nativeProbingRoots;
private readonly Lazy<DependencyProvider> _dependencies;

internal ScriptOptions ScriptOptions =
ScriptOptions.Default
Expand Down Expand Up @@ -74,7 +73,6 @@ public CSharpKernel() : base(DefaultKernelName)
RegisterForDisposal(() =>
{
ScriptState = null;
(_dependencies as IDisposable)?.Dispose();
});
}

Expand All @@ -86,21 +84,36 @@ public Task<bool> IsCompleteSubmissionAsync(string code)
return Task.FromResult(SyntaxFactory.IsCompleteSubmission(syntaxTree));
}

public override bool TryGetVariable(
public override bool TryGetVariable<T>(
string name,
out object value)
out T value)
{
if (ScriptState?.Variables
.LastOrDefault(v => v.Name == name) is { } variable)
{
value = variable.Value;
value = (T) variable.Value;
return true;
}

value = default;
return false;
}

public override async Task SetVariableAsync(string name, object value)
{
var csharpTypeDeclaration = new StringWriter();

value.GetType().WriteCSharpDeclarationTo(csharpTypeDeclaration);



await RunAsync($"{csharpTypeDeclaration} {name} = default;");

var scriptVariable = ScriptState.GetVariable(name);

scriptVariable.Value = value;
}

public override Task<LspResponse> LspMethod(string methodName, JObject request)
{
LspResponse result;
Expand All @@ -121,13 +134,13 @@ public override Task<LspResponse> LspMethod(string methodName, JObject request)

public TextDocumentHoverResponse TextDocumentHover(HoverParams hoverParams)
{
return new TextDocumentHoverResponse()
return new TextDocumentHoverResponse
{
Contents = new MarkupContent()
Contents = new MarkupContent
{
Kind = MarkupKind.Markdown,
Value = $"textDocument/hover at position ({hoverParams.Position.Line}, {hoverParams.Position.Character}) with `markdown`",
},
}
};
}

Expand Down Expand Up @@ -160,33 +173,16 @@ protected override async Task HandleSubmitCode(

if (!context.CancellationToken.IsCancellationRequested)
{
ScriptOptions = ScriptOptions.WithMetadataResolver(
ScriptMetadataResolver.Default.WithBaseDirectory(
Directory.GetCurrentDirectory()));

try
{
if (ScriptState == null)
{
ScriptState = await CSharpScript.RunAsync(
code,
ScriptOptions,
cancellationToken: context.CancellationToken)
.UntilCancelled(context.CancellationToken);
}
else
{
ScriptState = await ScriptState.ContinueWithAsync(
code,
ScriptOptions,
catchException: e =>
{
exception = e;
return true;
},
cancellationToken: context.CancellationToken)
.UntilCancelled(context.CancellationToken);
}
await RunAsync(
code,
context.CancellationToken,
e =>
{
exception = e;
return true;
});
}
catch (CompilationErrorException cpe)
{
Expand Down Expand Up @@ -232,6 +228,34 @@ protected override async Task HandleSubmitCode(
}
}

private async Task RunAsync(
string code,
CancellationToken cancellationToken = default,
Func<Exception, bool> catchException = default)
{
ScriptOptions = ScriptOptions.WithMetadataResolver(
ScriptMetadataResolver.Default.WithBaseDirectory(
Directory.GetCurrentDirectory()));

if (ScriptState == null)
{
ScriptState = await CSharpScript.RunAsync(
code,
ScriptOptions,
cancellationToken: cancellationToken)
.UntilCancelled(cancellationToken);
}
else
{
ScriptState = await ScriptState.ContinueWithAsync(
code,
ScriptOptions,
catchException: catchException,
cancellationToken: cancellationToken)
.UntilCancelled(cancellationToken);
}
}

protected override async Task HandleRequestCompletion(
RequestCompletion requestCompletion,
KernelInvocationContext context)
Expand Down Expand Up @@ -308,7 +332,6 @@ await _extensionLoader.LoadFromDirectoryAsync(
context);
}

private Lazy<DependencyProvider> _dependencies;

private DependencyProvider GetDependencyProvider()
{
Expand All @@ -323,27 +346,25 @@ private DependencyProvider GetDependencyProvider()
throw new ArgumentNullException(nameof(_nativeProbingRoots));
}

return new DependencyProvider(_assemblyProbingPaths, _nativeProbingRoots);
var dependencyProvider = new DependencyProvider(
_assemblyProbingPaths,
_nativeProbingRoots);

RegisterForDisposal(dependencyProvider);

return dependencyProvider;
}

// Set assemblyProbingPaths, nativeProbingRoots for Kernel.
// These values are functions that return the list of discovered assemblies, and package roots
// They are used by the dependecymanager for Assembly and Native dll resolving
void ISupportNuget.Initialize(AssemblyResolutionProbe assemblyProbingPaths, NativeResolutionProbe nativeProbingRoots)
{
if(assemblyProbingPaths == null)
{
throw new ArgumentNullException(nameof(assemblyProbingPaths));
}
if (nativeProbingRoots == null)
{
throw new ArgumentNullException(nameof(nativeProbingRoots));
}
_assemblyProbingPaths = assemblyProbingPaths;
_nativeProbingRoots = nativeProbingRoots;
_assemblyProbingPaths = assemblyProbingPaths ?? throw new ArgumentNullException(nameof(assemblyProbingPaths));
_nativeProbingRoots = nativeProbingRoots ?? throw new ArgumentNullException(nameof(nativeProbingRoots));
}

void ISupportNuget.RegisterNugetResolvedPackageReferences(IReadOnlyList<ResolvedPackageReference> resolvedReferences)
void ISupportNuget.RegisterResolvedPackageReferences(IReadOnlyList<ResolvedPackageReference> resolvedReferences)
{
var references = resolvedReferences
.SelectMany(r => r.AssemblyPaths)
Expand Down
6 changes: 0 additions & 6 deletions Microsoft.DotNet.Interactive.CSharp/CSharpKernelExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
// 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.Invocation;
using System.CommandLine.Parsing;
using System.CommandLine.Rendering;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Events;
using Microsoft.DotNet.Interactive.Formatting;
using static Microsoft.DotNet.Interactive.Formatting.PocketViewTags;

namespace Microsoft.DotNet.Interactive.CSharp
{
Expand Down
Loading