Inject the csi directly into my master.

This commit is contained in:
Pieter-Jan Briers
2021-02-23 01:39:33 +01:00
parent 6bd5814f4a
commit f0366531ef
3 changed files with 188 additions and 41 deletions

View File

@@ -1,5 +1,7 @@
#if CLIENT_SCRIPTING
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
@@ -36,6 +38,8 @@ namespace Robust.Client.Console
private readonly ScriptGlobals _globals;
private ScriptState? _state;
private (string[] imports, string code)? _autoImportRepeatBuffer;
public ScriptConsoleClient()
{
Title = Loc.GetString("Robust C# Interactive (CLIENT)");
@@ -54,38 +58,56 @@ namespace Robust.Client.Console
var code = InputBar.Text;
InputBar.Clear();
// Remove > or . at the end of the output panel.
OutputPanel.RemoveEntry(^1);
_inputBuffer.AppendLine(code);
_linesEntered += 1;
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()), ScriptInstanceShared.ParseOptions);
if (!SyntaxFactory.IsCompleteSubmission(tree))
if (_autoImportRepeatBuffer.HasValue && code == "y")
{
if (_linesEntered == 1)
var (imports, repeatCode) = _autoImportRepeatBuffer.Value;
var sb = new StringBuilder();
foreach (var import in imports)
{
OutputPanel.AddText($"> {code}");
sb.AppendFormat("using {0};\n", import);
}
else
{
OutputPanel.AddText($". {code}");
}
OutputPanel.AddText(".");
return;
sb.Append(repeatCode);
code = sb.ToString();
}
code = _inputBuffer.ToString().Trim();
// Remove echo of partial submission from the output panel.
for (var i = 1; i < _linesEntered; i++)
else
{
// Remove > or . at the end of the output panel.
OutputPanel.RemoveEntry(^1);
}
_inputBuffer.Clear();
_linesEntered = 0;
_inputBuffer.AppendLine(code);
_linesEntered += 1;
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()),
ScriptInstanceShared.ParseOptions);
if (!SyntaxFactory.IsCompleteSubmission(tree))
{
if (_linesEntered == 1)
{
OutputPanel.AddText($"> {code}");
}
else
{
OutputPanel.AddText($". {code}");
}
OutputPanel.AddText(".");
return;
}
code = _inputBuffer.ToString().Trim();
// Remove echo of partial submission from the output panel.
for (var i = 1; i < _linesEntered; i++)
{
OutputPanel.RemoveEntry(^1);
}
_inputBuffer.Clear();
_linesEntered = 0;
}
Script newScript;
@@ -135,6 +157,8 @@ namespace Robust.Client.Console
OutputPanel.AddMessage(msg);
OutputPanel.AddText(">");
PromptAutoImports(e.Diagnostics, code);
return;
}
@@ -155,6 +179,17 @@ namespace Robust.Client.Console
OutputPanel.AddText(">");
}
private void PromptAutoImports(IEnumerable<Diagnostic> diags, string code)
{
if (!ScriptInstanceShared.CalcAutoImports(_reflectionManager, diags, out var found))
return;
OutputPanel.AddText($"Auto-import {string.Join(", ", found)} (enter 'y')?");
_autoImportRepeatBuffer = (found.ToArray(), code);
}
private sealed class ScriptGlobalsImpl : ScriptGlobals
{
private readonly ScriptConsoleClient _owner;

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
@@ -76,7 +77,7 @@ namespace Robust.Server.Scripting
return;
}
if (!_conGroupController.CanViewVar(session))
if (!_conGroupController.CanScript(session))
{
Logger.WarningS("script", "Client {0} tried to access Scripting without permissions.", session);
_netManager.ServerSendMessage(reply, message.MsgChannel);
@@ -108,7 +109,7 @@ namespace Robust.Server.Scripting
return;
}
if (!_conGroupController.CanViewVar(session))
if (!_conGroupController.CanScript(session))
{
Logger.WarningS("script", "Client {0} tried to access Scripting without permissions.", session);
return;
@@ -125,23 +126,40 @@ namespace Robust.Server.Scripting
var code = message.Code;
instance.InputBuffer.AppendLine(code);
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(instance.InputBuffer.ToString()),
ScriptInstanceShared.ParseOptions);
if (!SyntaxFactory.IsCompleteSubmission(tree))
if (code == "y" && instance.AutoImportRepeatBuffer.HasValue)
{
replyMessage.WasComplete = false;
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
return;
var (imports, repeatCode) = instance.AutoImportRepeatBuffer.Value;
var sb = new StringBuilder();
foreach (var import in imports)
{
sb.AppendFormat("using {0};\n", import);
}
sb.Append(repeatCode);
code = sb.ToString();
replyMessage.WasComplete = true;
}
else
{
instance.InputBuffer.AppendLine(code);
replyMessage.WasComplete = true;
var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(instance.InputBuffer.ToString()),
ScriptInstanceShared.ParseOptions);
code = instance.InputBuffer.ToString().Trim();
if (!SyntaxFactory.IsCompleteSubmission(tree))
{
replyMessage.WasComplete = false;
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
return;
}
instance.InputBuffer.Clear();
replyMessage.WasComplete = true;
code = instance.InputBuffer.ToString().Trim();
instance.InputBuffer.Clear();
}
Script newScript;
@@ -188,6 +206,8 @@ namespace Robust.Server.Scripting
msg.AddText("\n");
}
PromptAutoImports(e.Diagnostics, code, msg, instance);
replyMessage.Response = msg;
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
return;
@@ -217,6 +237,21 @@ namespace Robust.Server.Scripting
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
}
private void PromptAutoImports(
IEnumerable<Diagnostic> diags,
string code,
FormattedMessage output,
ScriptInstance instance)
{
if (!ScriptInstanceShared.CalcAutoImports(_reflectionManager, diags, out var found))
return;
output.AddText($"Auto-import {string.Join(", ", found)} (enter 'y')?");
instance.AutoImportRepeatBuffer = (found.ToArray(), code);
}
private sealed class ScriptInstance
{
public Workspace HighlightWorkspace { get; } = new AdhocWorkspace();
@@ -227,6 +262,8 @@ namespace Robust.Server.Scripting
public ScriptGlobals Globals { get; }
public ScriptState? State { get; set; }
public (string[] imports, string code)? AutoImportRepeatBuffer;
public ScriptInstance()
{
Globals = new ScriptGlobalsImpl(this);

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using Lidgren.Network;
using Microsoft.CodeAnalysis;
@@ -23,6 +25,7 @@ namespace Robust.Shared.Scripting
new(kind: SourceCodeKind.Script, languageVersion: LanguageVersion.Latest);
private static readonly Func<Script, bool> _hasReturnValue;
private static readonly Func<Diagnostic, IReadOnlyList<object?>?> _getDiagnosticArguments;
private static readonly string[] _defaultImports =
{
@@ -47,10 +50,23 @@ namespace Robust.Shared.Scripting
// Fallback path in case they remove that.
// The method literally has a // TODO: remove
_hasReturnValue = _ => true;
return;
}
else
{
_hasReturnValue = (Func<Script, bool>) Delegate.CreateDelegate(typeof(Func<Script, bool>), method);
}
_hasReturnValue = (Func<Script, bool>) Delegate.CreateDelegate(typeof(Func<Script, bool>), method);
// Also internal and we need it.
var prop = typeof(Diagnostic).GetProperty("Arguments", BindingFlags.Instance | BindingFlags.NonPublic);
if (prop == null)
{
_getDiagnosticArguments = _ => null;
}
else
{
var moment = prop.GetMethod!;
_getDiagnosticArguments = moment.CreateDelegate<Func<Diagnostic, IReadOnlyList<object?>?>>();
}
// Run this async so that Roslyn can "warm up" in another thread while you're typing in your first line,
// so the hang when you hit enter is less bad.
@@ -79,6 +95,11 @@ namespace Robust.Shared.Scripting
return _hasReturnValue(script);
}
public static IReadOnlyList<object?>? GetDiagnosticArgs(Diagnostic diag)
{
return _getDiagnosticArguments(diag);
}
public static void AddWithSyntaxHighlighting(Script script, FormattedMessage msg, string code,
Workspace workspace)
{
@@ -139,6 +160,60 @@ namespace Robust.Shared.Scripting
return list;
}
private static IEnumerable<Assembly> GetAutoImportAssemblies(IReflectionManager refl)
{
return GetDefaultReferences(refl).Union(
AssemblyLoadContext.Default.Assemblies.Where(c => c.GetName().Name!.StartsWith("System."))
);
}
public static bool CalcAutoImports(
IReflectionManager refl,
IEnumerable<Diagnostic> diags,
[NotNullWhen(true)] out HashSet<string>? found)
{
var missing = new List<string>();
foreach (var diag in diags.Where(c => c.Id == "CS0103" || c.Id == "CS0246"))
{
var args = GetDiagnosticArgs(diag);
if (args == null)
{
found = null;
return false;
}
missing.Add((string) args[0]!);
}
if (missing.Count == 0)
{
found = null;
return false;
}
found = new HashSet<string>();
var assemblies = ScriptInstanceShared.GetAutoImportAssemblies(refl).ToArray();
foreach (var m in missing)
{
foreach (var assembly in assemblies)
{
foreach (var type in assembly.DefinedTypes)
{
if (type.IsPublic && type.Name == m)
{
found.Add(type.Namespace!);
goto nextMissing;
}
}
}
nextMissing: ;
}
return true;
}
public static ScriptOptions GetScriptOptions(IReflectionManager reflectionManager)
{
return ScriptOptions.Default