Skip to content

Instantly share code, notes, and snippets.

@20chan
Last active March 19, 2020 05:57
Show Gist options
  • Save 20chan/1199b5100c96fc146d6aff8f454bba5a to your computer and use it in GitHub Desktop.
Save 20chan/1199b5100c96fc146d6aff8f454bba5a to your computer and use it in GitHub Desktop.
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Microsoft.CSharp;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
public class UnitySniper : EditorWindow {
public Object[] parameters;
public string codeName;
public string code;
public string output;
public string usingCode = "using UnityEngine;\nusing System.Linq;";
public Object inspect;
public List<Snippet> snippets;
private bool running, runComplete;
private bool lastError;
private Func<bool> compileCallback;
[MenuItem("Sniper/Open")]
static void Init() {
GetWindow<UnitySniper>();
}
private void OnEnable() {
var data = EditorPrefs.GetString("SniperSnippets", "{}");
snippets = JsonUtility.FromJson<Snippets>(data).snippets;
}
private void OnGUI() {
if (running && runComplete) {
running = false;
lastError = compileCallback();
}
EditorGUILayout.LabelField("Code Sniper", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
var serial = new SerializedObject(this);
var parametersProp = serial.FindProperty(nameof(parameters));
EditorGUILayout.PropertyField(parametersProp, true);
serial.ApplyModifiedProperties();
EditorGUILayout.LabelField("usings");
usingCode = EditorGUILayout.TextArea(usingCode);
codeName = EditorGUILayout.TextField("code", codeName);
code = EditorGUILayout.TextArea(code, GUILayout.ExpandHeight(true));
EditorGUILayout.LabelField("output");
EditorGUILayout.TextArea(output);
var buttonStyle = new GUIStyle(GUI.skin.button);
var runningButtonColor = new GUIStyle(GUI.skin.button);
runningButtonColor.normal.textColor = Color.black;
var errorButtonColor = new GUIStyle(GUI.skin.button);
errorButtonColor.normal.textColor = Color.red;
var runStyle = running
? runningButtonColor
: lastError
? errorButtonColor
: buttonStyle;
GUI.enabled = !running;
if (GUILayout.Button("Run", runStyle, GUILayout.Height(40))) {
new Thread(() => {
running = true;
runComplete = false;
try {
compileCallback = Execute(code);
} finally {
runComplete = true;
}
}).Start();
}
GUI.enabled = true;
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(GUILayout.Width(200));
for (var i = 0; i < snippets.Count; i++) {
var s = snippets[i];
EditorGUILayout.BeginHorizontal();
GUI.enabled = !running;
if (GUILayout.Button(s.name, GUILayout.ExpandHeight(true))) {
new Thread(() => {
running = true;
runComplete = false;
try {
compileCallback = Execute(s.code);
} finally {
runComplete = true;
}
}).Start();
}
GUI.enabled = true;
EditorGUILayout.BeginVertical(GUILayout.Width(50));
if (GUILayout.Button("Load")) {
codeName = s.name;
code = s.code;
}
if (GUILayout.Button("Code")) {
code = s.code;
}
if (GUILayout.Button("Save")) {
s.name = codeName;
s.code = code;
Save();
}
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(GUILayout.Width(10));
if (GUILayout.Button("▲")) {
if (i != 0) {
snippets.RemoveAt(i);
snippets.Insert(i - 1, s);
Save();
}
}
if (GUILayout.Button("x")) {
snippets.RemoveAt(i);
i--;
Save();
}
if (GUILayout.Button("▼")) {
if (i != snippets.Count - 1) {
snippets.RemoveAt(i);
snippets.Insert(i + 1, s);
Save();
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
if (GUILayout.Button("Add")) {
snippets.Add(new Snippet { name = codeName, code = code });
Save();
}
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
var inspectProp = serial.FindProperty(nameof(inspect));
EditorGUILayout.LabelField("inspect", GUILayout.Width(50));
EditorGUILayout.PropertyField(inspectProp, GUIContent.none, true);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
private void Save() {
var data = JsonUtility.ToJson(new Snippets { snippets = snippets });
EditorPrefs.SetString("SniperSnippets", data);
}
private Func<bool> Execute(string code) {
const string prefix = "namespace AUTONAMESPACE { public class AUTOCLASS { public UnityEngine.Object inspect; public System.Text.StringBuilder AUTOSB = new System.Text.StringBuilder(); public void AUTOMETHOD(UnityEngine.Object[] parameters) {";
const string postfix = "} public void print(object o) { AUTOSB.AppendLine(o.ToString()); } } }";
var fullCode = string.Concat(usingCode, prefix, code, postfix);
var provider = new CSharpCodeProvider();
var options = new CompilerParameters {
GenerateInMemory = true,
GenerateExecutable = false,
};
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
if (assembly.IsDynamic) continue;
options.ReferencedAssemblies.Add(assembly.Location);
}
var result = provider.CompileAssemblyFromSource(options, fullCode);
var hasError = false;
foreach (CompilerError error in result.Errors) {
if (!error.IsWarning) {
hasError = true;
break;
}
}
if (hasError) {
var sb = new StringBuilder();
sb.AppendLine("COMPILE ERROR");
foreach (CompilerError error in result.Errors) {
sb.AppendFormat("Error {0}: {1}\n", error.ErrorNumber, error.ErrorText);
}
output = sb.ToString();
return () => {
output = sb.ToString();
return false;
};
}
var instance = result.CompiledAssembly.CreateInstance("AUTONAMESPACE.AUTOCLASS");
var method = instance.GetType().GetMethod("AUTOMETHOD");
return () => {
try {
method.Invoke(instance, new object[] { parameters });
} catch (Exception ex) {
var sb = new StringBuilder();
sb.AppendLine("RUNTIME ERROR");
sb.Append(ex.ToString());
output = sb.ToString();
return false;
}
var sbField = instance.GetType().GetField("AUTOSB");
output = sbField.GetValue(instance).ToString();
var inspectField = instance.GetType().GetField("inspect");
inspect = (Object)inspectField.GetValue(instance);
return true;
};
}
[Serializable]
public class Snippets {
public List<Snippet> snippets;
}
[Serializable]
public class Snippet {
public string name;
public string code;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment