Skip to content

Commit

Permalink
Inject the csi directly into my master.
Browse files Browse the repository at this point in the history
  • Loading branch information
PJB3005 committed Feb 23, 2021
1 parent 6bd5814 commit f036653
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 38 deletions.
81 changes: 58 additions & 23 deletions Robust.Client/Console/ScriptConsoleClient.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -36,6 +38,8 @@ internal sealed class ScriptConsoleClient : ScriptConsole
private readonly ScriptGlobals _globals;
private ScriptState? _state;

private (string[] imports, string code)? _autoImportRepeatBuffer;

public ScriptConsoleClient()
{
Title = Loc.GetString("Robust C# Interactive (CLIENT)");
Expand All @@ -54,39 +58,57 @@ protected override async void Run()
var code = InputBar.Text;
InputBar.Clear();

// Remove > or . at the end of the output panel.
OutputPanel.RemoveEntry(^1);

_inputBuffer.AppendLine(code);
_linesEntered += 1;
if (_autoImportRepeatBuffer.HasValue && code == "y")
{
var (imports, repeatCode) = _autoImportRepeatBuffer.Value;
var sb = new StringBuilder();
foreach (var import in imports)
{
sb.AppendFormat("using {0};\n", import);
}

var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(_inputBuffer.ToString()), ScriptInstanceShared.ParseOptions);
sb.Append(repeatCode);

if (!SyntaxFactory.IsCompleteSubmission(tree))
code = sb.ToString();
}
else
{
if (_linesEntered == 1)
// 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))
{
OutputPanel.AddText($"> {code}");
if (_linesEntered == 1)
{
OutputPanel.AddText($"> {code}");
}
else
{
OutputPanel.AddText($". {code}");
}

OutputPanel.AddText(".");
return;
}
else

code = _inputBuffer.ToString().Trim();

// Remove echo of partial submission from the output panel.
for (var i = 1; i < _linesEntered; i++)
{
OutputPanel.AddText($". {code}");
OutputPanel.RemoveEntry(^1);
}
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;
}

_inputBuffer.Clear();
_linesEntered = 0;

Script newScript;

if (_state != null)
Expand Down Expand Up @@ -135,6 +157,8 @@ protected override async void Run()

OutputPanel.AddMessage(msg);
OutputPanel.AddText(">");

PromptAutoImports(e.Diagnostics, code);
return;
}

Expand All @@ -155,6 +179,17 @@ protected override async void Run()
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;
Expand Down
63 changes: 50 additions & 13 deletions Robust.Server/Scripting/ScriptHost.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -76,7 +77,7 @@ private void ReceiveScriptStart(MsgScriptStart message)
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);
Expand Down Expand Up @@ -108,7 +109,7 @@ private async void ReceiveScriptEval(MsgScriptEval message)
return;
}

if (!_conGroupController.CanViewVar(session))
if (!_conGroupController.CanScript(session))
{
Logger.WarningS("script", "Client {0} tried to access Scripting without permissions.", session);
return;
Expand All @@ -125,23 +126,40 @@ private async void ReceiveScriptEval(MsgScriptEval message)

var code = message.Code;

instance.InputBuffer.AppendLine(code);
if (code == "y" && instance.AutoImportRepeatBuffer.HasValue)
{
var (imports, repeatCode) = instance.AutoImportRepeatBuffer.Value;
var sb = new StringBuilder();
foreach (var import in imports)
{
sb.AppendFormat("using {0};\n", import);
}

var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(instance.InputBuffer.ToString()),
ScriptInstanceShared.ParseOptions);
sb.Append(repeatCode);

if (!SyntaxFactory.IsCompleteSubmission(tree))
{
replyMessage.WasComplete = false;
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
return;
code = sb.ToString();
replyMessage.WasComplete = true;
}
else
{
instance.InputBuffer.AppendLine(code);

var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(instance.InputBuffer.ToString()),
ScriptInstanceShared.ParseOptions);

if (!SyntaxFactory.IsCompleteSubmission(tree))
{
replyMessage.WasComplete = false;
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
return;
}

replyMessage.WasComplete = true;
replyMessage.WasComplete = true;

code = instance.InputBuffer.ToString().Trim();
code = instance.InputBuffer.ToString().Trim();

instance.InputBuffer.Clear();
instance.InputBuffer.Clear();
}

Script newScript;

Expand Down Expand Up @@ -188,6 +206,8 @@ private async void ReceiveScriptEval(MsgScriptEval message)
msg.AddText("\n");
}

PromptAutoImports(e.Diagnostics, code, msg, instance);

replyMessage.Response = msg;
_netManager.ServerSendMessage(replyMessage, message.MsgChannel);
return;
Expand Down Expand Up @@ -217,6 +237,21 @@ private async void ReceiveScriptEval(MsgScriptEval message)
_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();
Expand All @@ -227,6 +262,8 @@ private sealed class ScriptInstance
public ScriptGlobals Globals { get; }
public ScriptState? State { get; set; }

public (string[] imports, string code)? AutoImportRepeatBuffer;

public ScriptInstance()
{
Globals = new ScriptGlobalsImpl(this);
Expand Down
79 changes: 77 additions & 2 deletions Robust.Shared.Scripting/ScriptInstanceShared.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -23,6 +25,7 @@ internal static class ScriptInstanceShared
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 =
{
Expand All @@ -47,10 +50,23 @@ static ScriptInstanceShared()
// 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.
Expand Down Expand Up @@ -79,6 +95,11 @@ public static bool HasReturnValue(Script script)
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)
{
Expand Down Expand Up @@ -139,6 +160,60 @@ private static IEnumerable<Assembly> GetDefaultReferences(IReflectionManager ref
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
Expand Down

0 comments on commit f036653

Please sign in to comment.