Files
RobustToolbox/Robust.Client/Console/WatchWindow.cs
2023-07-08 14:08:26 +10:00

203 lines
6.3 KiB
C#

#if CLIENT_SCRIPTING
using System;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
using Microsoft.CodeAnalysis.Scripting;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Scripting;
using Robust.Shared.Timing;
using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Robust.Client.Console
{
public sealed class WatchWindow : DefaultWindow
{
private readonly IReflectionManager _reflectionManager;
private readonly IDependencyCollection _dependency;
private readonly BoxContainer _watchesVBox;
private readonly LineEdit _addWatchEdit;
private readonly Button _addWatchButton;
public WatchWindow()
{
_reflectionManager = IoCManager.Resolve<IReflectionManager>();
_dependency = IoCManager.Resolve<IDependencyCollection>();
ScriptInstanceShared.InitDummy();
Title = "Watch Window";
var mainVBox = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
MinSize = new(500, 300),
Children =
{
(_watchesVBox = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
VerticalExpand = true
}),
new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
(_addWatchEdit = new HistoryLineEdit
{
HorizontalExpand = true,
PlaceHolder = "Add watch (C# interactive)"
}),
(_addWatchButton = new Button
{
Text = "Add"
})
}
}
},
};
_addWatchButton.OnPressed += _ => AddWatch();
_addWatchEdit.OnTextEntered += _ => AddWatch();
Contents.AddChild(mainVBox);
SetSize = new(300, 300);
}
private void AddWatch()
{
var code = _addWatchEdit.Text;
if (string.IsNullOrWhiteSpace(code))
{
return;
}
var options = ScriptInstanceShared.GetScriptOptions(_reflectionManager);
var script = CSharpScript.Create(code, options, typeof(ScriptGlobalsShared));
ScriptRunner<object> @delegate;
try
{
@delegate = script.CreateDelegate();
}
catch (CompilationErrorException compilationError)
{
_watchesVBox.AddChild(new CompilationErrorControl(string.Join('\n', compilationError.Diagnostics)));
return;
}
var control = new WatchControl(@delegate, _dependency);
_watchesVBox.AddChild(control);
_addWatchEdit.Clear();
}
private sealed class WatchControl : Control
{
private readonly ScriptRunner<object> _runner;
private readonly ScriptGlobalsImpl _globals;
private readonly Label _outputLabel;
public WatchControl(ScriptRunner<object> runner, IDependencyCollection dependency)
{
Button delButton;
_runner = runner;
_globals = new ScriptGlobalsImpl(dependency);
AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
(_outputLabel = new Label
{
HorizontalExpand = true,
ClipText = true
}),
(delButton = new Button
{
Text = "Remove"
}),
}
});
delButton.OnPressed += _ => Orphan();
}
protected override async void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
object obj;
try
{
obj = await _runner(_globals);
}
catch (Exception e)
{
_outputLabel.Text = CSharpObjectFormatter.Instance.FormatException(e);
return;
}
_outputLabel.Text = CSharpObjectFormatter.Instance.FormatObject(obj);
}
}
private sealed class ScriptGlobalsImpl : ScriptGlobalsShared
{
public ScriptGlobalsImpl(IDependencyCollection dependency) : base(dependency)
{
}
protected override void WriteSyntax(object toString)
{
// No-op: nothing to write to.
}
public override void write(object toString)
{
// No-op: nothing to write to.
}
public override void show(object obj)
{
// No-op: nothing to write to.
}
}
private sealed class CompilationErrorControl : Control
{
public CompilationErrorControl(string message)
{
Button delButton;
AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label
{
Text = message,
ClipText = true,
HorizontalExpand = true
},
(delButton = new Button {Text = "Remove"})
}
});
delButton.OnPressed += _ => Orphan();
}
}
}
}
#endif