Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add do-nothing SQL kernel to the builtin kernels in order to enable SQL language coloring #1105

Closed
wants to merge 5 commits into from
Closed
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 src/Microsoft.DotNet.Interactive.SqlServer/MsSqlKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class MsSqlKernel :
public MsSqlKernel(
string name,
string connectionString,
MsSqlServiceClient client) : base(name)
MsSqlServiceClient client) : base(name, "SQL")
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be "T-SQL" or "MSSQL" or something more specific.

{
if (string.IsNullOrWhiteSpace(connectionString))
{
Expand Down
43 changes: 43 additions & 0 deletions src/Microsoft.DotNet.Interactive.Tests/SqlKernelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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 FluentAssertions;
using System.Threading.Tasks;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Events;
using Microsoft.DotNet.Interactive.Tests.Utility;
using Xunit;

namespace Microsoft.DotNet.Interactive.Tests
{
public class SqlKernelTests
{
[Fact]
public async Task sql_kernel_emits_help_message_without_sql_server_extension_installed()
{
using var kernel = new CompositeKernel
{
new SqlKernel()
};

using var events = kernel.KernelEvents.ToSubscribedList();

var query = "select * from sys.databases";
await kernel.SendAsync(new SubmitCode($"#!sql\n\n{query}"));

var displayValue = events.Should()
.ContainSingle<DisplayedValueProduced>()
.Which;

var message = (string)displayValue.Value;

message.Should().NotContain(query);

// Should contain instructions for how to install SqlServer extension package
message.Should().Contain("#r Microsoft.DotNet.Interactive.SqlServer");

// Should contain instructions for how to get help message for MSSQL kernel
message.Should().Contain("#!connect mssql -h");
}
}
}
6 changes: 5 additions & 1 deletion src/Microsoft.DotNet.Interactive/Kernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public abstract partial class Kernel : IDisposable
private FrontendEnvironment _frontendEnvironment;
private ChooseKernelDirective _chooseKernelDirective;

protected Kernel(string name)
protected Kernel(string name, string language = null)
{
if (string.IsNullOrWhiteSpace(name))
{
Expand All @@ -44,6 +44,8 @@ protected Kernel(string name)

Name = name;

Language = language;

SubmissionParser = new SubmissionParser(this);

_disposables = new CompositeDisposable();
Expand Down Expand Up @@ -214,6 +216,8 @@ public FrontendEnvironment FrontendEnvironment

public string Name { get; set; }

public string Language { get; private set; }

public IReadOnlyCollection<ICommand> Directives => SubmissionParser.Directives;

public void AddDirective(Command command) => SubmissionParser.AddDirective(command);
Expand Down
23 changes: 23 additions & 0 deletions src/Microsoft.DotNet.Interactive/KernelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@ public static Kernel FindKernel(this Kernel kernel, string name)
};
}

public static Kernel FindKernel(this Kernel kernel, Func<Kernel, bool> selector)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should return IEnumerable<Kernel> since there can be more than one that matches.

It would be more straightforward to use VisitSubkernels and avoid looking at the ChooseKernelDirectives.

{
var root = kernel
.RecurseWhileNotNull(k => k switch
{
{ } kb => kb.ParentKernel,
_ => null
})
.LastOrDefault();

return root switch
{
_ when selector(kernel) => kernel,
CompositeKernel c =>
c.Directives
.OfType<ChooseKernelDirective>()
.Where(d => selector(d.Kernel))
.Select(d => d.Kernel)
.SingleOrDefault(),
_ => null
};
}

public static Task<KernelCommandResult> SendAsync(
this Kernel kernel,
KernelCommand command)
Expand Down
42 changes: 42 additions & 0 deletions src/Microsoft.DotNet.Interactive/SqlKernel.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 Microsoft.DotNet.Interactive.Commands;

namespace Microsoft.DotNet.Interactive
{
/* This kernel is used as a placeholder for the MSSQL kernel in order to enable SQL language coloring
* in the editor. Language grammars can only be defined for fixed kernel names, but MSSQL subkernels
* are user-defined via the #!connect magic command. So, this kernel is specified in addition to the
* user-defined kernel as a kind of "styling" kernel.
*/
public class SqlKernel :
Kernel,
IKernelCommandHandler<SubmitCode>
{
public const string DefaultKernelName = "sql";

public SqlKernel() : base(DefaultKernelName)
{
}

public Task HandleAsync(SubmitCode command, KernelInvocationContext context)
{
var sqlKernel = this.FindKernel(kernel => kernel.Language?.ToUpper() == "SQL");
if (sqlKernel == null)
{
context.Display(@"
A SQL kernel is not currently defined.

SQL kernels are provided as part of the Microsoft.DotNet.Interactive.SqlServer package, which can be installed by using the following nuget command:
#r Microsoft.DotNet.Interactive.SqlServer
Copy link
Contributor

Choose a reason for hiding this comment

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

The #r directive should be updated to #r "nuget:Microsoft.DotNet.Interactive.SqlServer,*-*".

Formatted as Markdown, you'll want:

image


Once installed, you can find more info about creating a SQL kernel and running queries by running the following help command:
#!connect mssql -h
");
Copy link
Contributor

@jonsequitur jonsequitur Feb 26, 2021

Choose a reason for hiding this comment

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

If the message is emitted as "text/markdown" we can include a link to the package on NuGet.org, and people can find additional information that way.

}
return Task.CompletedTask;
}
}
}
12 changes: 12 additions & 0 deletions src/dotnet-interactive-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@
"PowerShell (.NET Interactive)"
],
"configuration": "./syntaxes/powershell.language-configuration.json"
},
{
"id": "dotnet-interactive.sql",
"aliases": [
"SQL (.NET Interactive)"
],
"configuration": "./syntaxes/sql.language-configuration.json"
}
],
"grammars": [
Expand Down Expand Up @@ -256,6 +263,11 @@
"language": "dotnet-interactive.pwsh",
"scopeName": "source.dotnet-interactive.powershell",
"path": "./syntaxes/source.dotnet-interactive.powershell.tmGrammar.json"
},
{
"language": "dotnet-interactive.sql",
"scopeName": "source.dotnet-interactive.sql",
"path": "./syntaxes/source.dotnet-interactive.sql.tmGrammar.json"
}
]
},
Expand Down
3 changes: 2 additions & 1 deletion src/dotnet-interactive-vscode/src/interactiveNotebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export const notebookCellLanguages: Array<string> = [
'dotnet-interactive.fsharp',
'dotnet-interactive.html',
'dotnet-interactive.javascript',
'dotnet-interactive.pwsh'
'dotnet-interactive.pwsh',
'dotnet-interactive.sql'
];

const notebookLanguagePrefix = 'dotnet-interactive.';
Expand Down
12 changes: 10 additions & 2 deletions src/dotnet-interactive-vscode/src/tests/unit/grammar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ describe('TextMate grammar tests', async () => {
'#!markdown',
'#!md',
'#!powershell',
'#!pwsh'
'#!pwsh',
'#!sql'
];
const tokens = await getTokens(text);
expect(tokens).to.deep.equal([
Expand Down Expand Up @@ -157,6 +158,12 @@ describe('TextMate grammar tests', async () => {
tokenText: '#!pwsh',
scopes: ['source.dotnet-interactive', 'language.switch.powershell']
}
],
[
{
tokenText: '#!sql',
scopes: ['source.dotnet-interactive', 'language.switch.sql']
}
]
]);
});
Expand Down Expand Up @@ -207,7 +214,8 @@ describe('TextMate grammar tests', async () => {
'html',
'javascript',
'markdown',
'powershell'
'powershell',
'sql'
];

for (const language of allLanguages) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"patterns": [
{
"name": "comment.line.magic-commands",
"begin": "^(#!)(?!([cf](#|s(harp)?)|powershell|pwsh|html|javascript|js|markdown|md))",
"begin": "^(#!)(?!([cf](#|s(harp)?)|powershell|pwsh|html|javascript|js|markdown|md|sql))",
"end": "(?<=$)",
"beginCaptures": {
"1": {
Expand Down Expand Up @@ -68,4 +68,4 @@
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"scopeName": "source.dotnet-interactive.sql",
"patterns": [
{
"include": "source.dotnet-interactive.magic-commands"
},
{
"include": "source.dotnet-interactive"
},
{
"include": "source.sql"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"patterns": [
{
"begin": "^#!cs(harp)?\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh)\\s+$)",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.csharp",
"patterns": [
{
Expand All @@ -13,7 +13,7 @@
},
{
"begin": "^#!fs(harp)?\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh)\\s+$)",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.fsharp",
"patterns": [
{
Expand All @@ -23,7 +23,7 @@
},
{
"begin": "^#!html\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh)\\s+$)",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.html",
"patterns": [
{
Expand All @@ -33,7 +33,7 @@
},
{
"begin": "^#!(javascript|js)\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh)\\s+$)",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.javascript",
"patterns": [
{
Expand All @@ -43,7 +43,7 @@
},
{
"begin": "^#!(markdown|md)\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh)\\s+$)",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.markdown",
"patterns": [
{
Expand All @@ -53,13 +53,23 @@
},
{
"begin": "^#!(powershell|pwsh)\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh)\\s+$)",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.powershell",
"patterns": [
{
"include": "source.dotnet-interactive.powershell"
}
]
},
{
"begin": "^#!sql\\s+$",
"end": "(?=^#!(cs(harp)?|fs(harp)?|html|javascript|js|markdown|md|powershell|pwsh|sql)\\s+$)",
"name": "language.switch.sql",
"patterns": [
{
"include": "source.dotnet-interactive.sql"
}
]
}
]
}
}
Loading