mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Inject the csi directly into my master.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user