Compare commits
11 Commits
0.4.1
...
4e885847a8
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e885847a8 | |||
|
0ca6b27a11
|
|||
| 9b8900d7a7 | |||
|
792bba4875
|
|||
|
1c39e146fb
|
|||
|
47bcd0be34
|
|||
|
445f731170
|
|||
|
c7aa30581d
|
|||
|
42b0b0acf9
|
|||
|
5230c620e8
|
|||
|
06a0ec28eb
|
14
Changelog.md
14
Changelog.md
@@ -1,5 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
[0.4.3]
|
||||||
|
|
||||||
|
- Removed references to the `Mod` class from SLP. This was the root of the multiplayer
|
||||||
|
connectivity issues. Multiplayer should now work with Slang installed.
|
||||||
|
|
||||||
|
[0.4.2]
|
||||||
|
|
||||||
|
- Removed all harmony patches as most functionality as been added into the
|
||||||
|
`IC10 Editor` mod
|
||||||
|
- IC10 runtime errors will have been reverted back to showing as IC10 line
|
||||||
|
numbers instead of Slang line numbers.
|
||||||
|
- The IC10 line should be easily mapped to a Slang line via the side-by-side
|
||||||
|
IC10 compilation view.
|
||||||
|
|
||||||
[0.4.1]
|
[0.4.1]
|
||||||
|
|
||||||
- Update syscalls for `loadSlot` and `setSlot` to support expressions instead of
|
- Update syscalls for `loadSlot` and `setSlot` to support expressions instead of
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<Name>Slang</Name>
|
<Name>Slang</Name>
|
||||||
<Author>JoeDiertay</Author>
|
<Author>JoeDiertay</Author>
|
||||||
<Version>0.4.1</Version>
|
<Version>0.4.3</Version>
|
||||||
<Description>
|
<Description>
|
||||||
[h1]Slang: High-Level Programming for Stationeers[/h1]
|
[h1]Slang: High-Level Programming for Stationeers[/h1]
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ A: Yes! Slang does not modify any existing IC10 code, it is only a compiler. As
|
|||||||
<Tag>Quality of Life</Tag>
|
<Tag>Quality of Life</Tag>
|
||||||
</Tags>
|
</Tags>
|
||||||
<DependsOn WorkshopHandle="3592775931" />
|
<DependsOn WorkshopHandle="3592775931" />
|
||||||
<OrderBefore WorkshopHandle="3592775931" />
|
<OrderAfter WorkshopHandle="3592775931" />
|
||||||
<InGameDescription><![CDATA[
|
<InGameDescription><![CDATA[
|
||||||
<size=30><color=#ffff00>Slang - High Level Language Compiler</color></size>
|
<size=30><color=#ffff00>Slang - High Level Language Compiler</color></size>
|
||||||
A modern programming experience for Stationeers. Write C-style code that compiles to IC10 instantly.
|
A modern programming experience for Stationeers. Write C-style code that compiles to IC10 instantly.
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ImGuiNET;
|
|
||||||
using StationeersIC10Editor;
|
using StationeersIC10Editor;
|
||||||
using StationeersIC10Editor.IC10;
|
using StationeersIC10Editor.IC10;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class SlangFormatter : ICodeFormatter
|
public class SlangFormatter : ICodeFormatter
|
||||||
{
|
{
|
||||||
|
public const string SLANG_SRC = "SLANG_SRC";
|
||||||
private CancellationTokenSource? _lspCancellationToken;
|
private CancellationTokenSource? _lspCancellationToken;
|
||||||
private object _tokenLock = new();
|
private object _tokenLock = new();
|
||||||
|
|
||||||
|
protected Editor? Ic10Editor = null;
|
||||||
private IC10CodeFormatter iC10CodeFormatter = new IC10CodeFormatter();
|
private IC10CodeFormatter iC10CodeFormatter = new IC10CodeFormatter();
|
||||||
private string ic10CompilationResult = "";
|
private string ic10CompilationResult = "";
|
||||||
private List<SourceMapEntry> ic10SourceMap = new();
|
private List<SourceMapEntry> ic10SourceMap = new();
|
||||||
|
|
||||||
// VS Code Dark Theme Palette
|
#region Colors
|
||||||
public static readonly uint ColorControl = ColorFromHTML("#C586C0"); // Pink (if, return, loop)
|
public static readonly uint ColorControl = ColorFromHTML("#C586C0"); // Pink (if, return, loop)
|
||||||
public static readonly uint ColorDeclaration = ColorFromHTML("#569CD6"); // Blue (let, device, fn)
|
public static readonly uint ColorDeclaration = ColorFromHTML("#569CD6"); // Blue (let, device, fn)
|
||||||
public static readonly uint ColorFunction = ColorFromHTML("#DCDCAA"); // Yellow (syscalls)
|
public static readonly uint ColorFunction = ColorFromHTML("#DCDCAA"); // Yellow (syscalls)
|
||||||
@@ -28,10 +28,8 @@ public class SlangFormatter : ICodeFormatter
|
|||||||
public static readonly uint ColorBoolean = ColorFromHTML("#569CD6"); // Blue (true/false)
|
public static readonly uint ColorBoolean = ColorFromHTML("#569CD6"); // Blue (true/false)
|
||||||
public static readonly uint ColorIdentifier = ColorFromHTML("#9CDCFE"); // Light Blue (variables)
|
public static readonly uint ColorIdentifier = ColorFromHTML("#9CDCFE"); // Light Blue (variables)
|
||||||
public static new readonly uint ColorDefault = ColorFromHTML("#D4D4D4"); // White (punctuation ; { } )
|
public static new readonly uint ColorDefault = ColorFromHTML("#D4D4D4"); // White (punctuation ; { } )
|
||||||
|
|
||||||
// Operators are often the same color as default text in VS Code Dark,
|
|
||||||
// but having a separate definition lets you tweak it (e.g. make them slightly darker or distinct)
|
|
||||||
public static readonly uint ColorOperator = ColorFromHTML("#D4D4D4");
|
public static readonly uint ColorOperator = ColorFromHTML("#D4D4D4");
|
||||||
|
#endregion
|
||||||
|
|
||||||
private HashSet<uint> _linesWithErrors = new();
|
private HashSet<uint> _linesWithErrors = new();
|
||||||
private int _lastLineCount = -1;
|
private int _lastLineCount = -1;
|
||||||
@@ -49,6 +47,11 @@ public class SlangFormatter : ICodeFormatter
|
|||||||
if (string.IsNullOrWhiteSpace(input))
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
return 0d;
|
return 0d;
|
||||||
|
|
||||||
|
if (input.Contains(SLANG_SRC))
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
// Run the compiler to get diagnostics
|
// Run the compiler to get diagnostics
|
||||||
var diagnostics = Marshal.DiagnoseSource(input);
|
var diagnostics = Marshal.DiagnoseSource(input);
|
||||||
|
|
||||||
@@ -75,35 +78,28 @@ public class SlangFormatter : ICodeFormatter
|
|||||||
|
|
||||||
public override string Compile()
|
public override string Compile()
|
||||||
{
|
{
|
||||||
return this.Lines.RawText;
|
if (!Marshal.CompileFromString(RawText, out var compilationResult, out var sourceMap))
|
||||||
|
{
|
||||||
|
return "Compilation Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return compilationResult + $"\n{EncodeSource(RawText, SLANG_SRC)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawLine(int lineIndex, TextRange selection, bool drawLineNumber = true)
|
public override void ResetCode(string code)
|
||||||
{
|
{
|
||||||
Vector2 cursorPos = ImGui.GetCursorScreenPos();
|
// for compatibility, we need to check for GlobalCode.SLANG_SRC
|
||||||
Vector2 space = ImGui.GetContentRegionAvail();
|
// `#SLANG_SRC:<code>`
|
||||||
base.DrawLine(lineIndex, selection, drawLineNumber);
|
// and replace with `# SLANG_SRC: <code>`
|
||||||
|
if (code.Contains(GlobalCode.SLANG_SRC))
|
||||||
var charWidth = Settings.CharWidth;
|
{
|
||||||
|
code = code.Replace(GlobalCode.SLANG_SRC, $"# {SLANG_SRC}: ");
|
||||||
var width = Mathf.Max(Lines.Width + 10.0f + LineNumberOffset * charWidth, space.x / 2);
|
}
|
||||||
|
if (code.Contains(SLANG_SRC))
|
||||||
ImGui
|
{
|
||||||
.GetWindowDrawList()
|
code = ExtractEncodedSource(code, SLANG_SRC);
|
||||||
.AddLine(
|
}
|
||||||
new Vector2(cursorPos.x + width + 4.5f * charWidth, cursorPos.y),
|
base.ResetCode(code);
|
||||||
new Vector2(
|
|
||||||
cursorPos.x + width + 4.5f * charWidth,
|
|
||||||
cursorPos.y + space.y + Settings.LineHeight
|
|
||||||
),
|
|
||||||
ColorLineNumber,
|
|
||||||
1.0f
|
|
||||||
);
|
|
||||||
|
|
||||||
cursorPos.x += width;
|
|
||||||
ImGui.SetCursorScreenPos(cursorPos);
|
|
||||||
if (lineIndex < iC10CodeFormatter.Lines.Count)
|
|
||||||
iC10CodeFormatter.DrawLine(lineIndex, new TextRange(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override StyledLine ParseLine(string line)
|
public override StyledLine ParseLine(string line)
|
||||||
@@ -198,7 +194,20 @@ public class SlangFormatter : ICodeFormatter
|
|||||||
|
|
||||||
private void UpdateIc10Formatter()
|
private void UpdateIc10Formatter()
|
||||||
{
|
{
|
||||||
iC10CodeFormatter.Editor = Editor;
|
var tab = Editor.ParentTab;
|
||||||
|
if (Ic10Editor == null)
|
||||||
|
{
|
||||||
|
iC10CodeFormatter = new IC10CodeFormatter();
|
||||||
|
Ic10Editor = new Editor(Editor.KeyHandler);
|
||||||
|
Ic10Editor.IsReadOnly = true;
|
||||||
|
iC10CodeFormatter.Editor = Ic10Editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tab.Editors.Count < 2)
|
||||||
|
{
|
||||||
|
tab.AddEditor(Ic10Editor);
|
||||||
|
}
|
||||||
|
|
||||||
var caretPos = Editor.CaretPos.Line;
|
var caretPos = Editor.CaretPos.Line;
|
||||||
|
|
||||||
// get the slang sourceMap at the current editor line
|
// get the slang sourceMap at the current editor line
|
||||||
@@ -210,37 +219,30 @@ public class SlangFormatter : ICodeFormatter
|
|||||||
// should be directly next to the compiled IC10 source line, and we should highlight the
|
// should be directly next to the compiled IC10 source line, and we should highlight the
|
||||||
// IC10 code that directly represents the Slang source
|
// IC10 code that directly represents the Slang source
|
||||||
|
|
||||||
iC10CodeFormatter.ResetCode(ic10CompilationResult);
|
Ic10Editor.ResetCode(ic10CompilationResult);
|
||||||
|
|
||||||
if (lines.Count() < 1)
|
if (lines.Count() < 1)
|
||||||
{
|
{
|
||||||
|
Ic10Editor.Selection = new TextRange
|
||||||
|
{
|
||||||
|
End = new TextPosition { Col = 0, Line = 0 },
|
||||||
|
Start = new TextPosition { Col = 0, Line = 0 },
|
||||||
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// get the total range of the IC10 source for the selected Slang line
|
// get the total range of the IC10 source for the selected Slang line
|
||||||
var max = lines.Max(line => line.Ic10Line);
|
var max = lines.Max(line => line.Ic10Line);
|
||||||
var min = lines.Min(line => line.Ic10Line);
|
var min = lines.Min(line => line.Ic10Line);
|
||||||
|
|
||||||
|
Ic10Editor.CaretPos = new TextPosition { Col = 0, Line = (int)max };
|
||||||
|
|
||||||
// highlight all the IC10 lines that are within the specified range
|
// highlight all the IC10 lines that are within the specified range
|
||||||
foreach (var index in Enumerable.Range((int)min, (int)(max - min) + 1))
|
Ic10Editor.Selection.Start = new TextPosition { Col = 0, Line = (int)min };
|
||||||
|
Ic10Editor.Selection.End = new TextPosition
|
||||||
{
|
{
|
||||||
var lineText = iC10CodeFormatter.Lines[index].Text;
|
Col = Ic10Editor.Lines[(int)max].Text.Length,
|
||||||
|
Line = (int)max,
|
||||||
var newLine = new StyledLine(
|
};
|
||||||
lineText,
|
|
||||||
[
|
|
||||||
new SemanticToken
|
|
||||||
{
|
|
||||||
Column = 0,
|
|
||||||
Length = lineText.Length,
|
|
||||||
Line = index,
|
|
||||||
Background = ColorIdentifier,
|
|
||||||
Color = ColorFromHTML("black"),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
iC10CodeFormatter.Lines[index] = newLine;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This runs on the Main Thread
|
// This runs on the Main Thread
|
||||||
|
|||||||
@@ -1,30 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Slang;
|
namespace Slang;
|
||||||
|
|
||||||
public static class GlobalCode
|
public static class GlobalCode
|
||||||
{
|
{
|
||||||
public const string SLANG_REF = "#SLANG_REF:";
|
/// <summary>
|
||||||
|
/// This is the OLD way of handling saving / loading. This has been replaced with a native
|
||||||
|
/// save / load from IC10 Editor. However; this needs to remain for compatibility with
|
||||||
|
/// previous versions of code not compiled with 0.4.2 or later.
|
||||||
|
/// </summary>
|
||||||
public const string SLANG_SRC = "#SLANG_SRC:";
|
public const string SLANG_SRC = "#SLANG_SRC:";
|
||||||
|
|
||||||
// This is a Dictionary of ENCODED source code, compressed
|
/// <summary>
|
||||||
// so that save file data is smaller
|
/// This Dictionary stores the source maps for the given SLANG_REF, where
|
||||||
private static Dictionary<Guid, string> codeDict = new();
|
/// the key is the IC10 line, and the value is a List of Slang ranges where that
|
||||||
|
/// line would have come from
|
||||||
// This Dictionary stores the source maps for the given SLANG_REF, where
|
/// </summary>
|
||||||
// the key is the IC10 line, and the value is a List of Slang ranges where that
|
|
||||||
// line would have come from
|
|
||||||
private static Dictionary<Guid, Dictionary<uint, List<Range>>> sourceMaps = new();
|
private static Dictionary<Guid, Dictionary<uint, List<Range>>> sourceMaps = new();
|
||||||
|
|
||||||
public static void ClearCache()
|
|
||||||
{
|
|
||||||
codeDict.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetSourceMap(Guid reference, List<SourceMapEntry> sourceMapEntries)
|
public static void SetSourceMap(Guid reference, List<SourceMapEntry> sourceMapEntries)
|
||||||
{
|
{
|
||||||
var builtDictionary = new Dictionary<uint, List<Range>>();
|
var builtDictionary = new Dictionary<uint, List<Range>>();
|
||||||
@@ -72,81 +66,4 @@ public static class GlobalCode
|
|||||||
slangSpan = foundRange[0];
|
slangSpan = foundRange[0];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetSource(Guid reference)
|
|
||||||
{
|
|
||||||
if (!codeDict.ContainsKey(reference))
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DecodeSource(codeDict[reference]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetSource(Guid reference, string source)
|
|
||||||
{
|
|
||||||
codeDict[reference] = EncodeSource(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string? GetEncoded(Guid reference)
|
|
||||||
{
|
|
||||||
if (!codeDict.ContainsKey(reference))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return codeDict[reference];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetEncoded(Guid reference, string encodedSource)
|
|
||||||
{
|
|
||||||
if (codeDict.ContainsKey(reference))
|
|
||||||
{
|
|
||||||
codeDict[reference] = encodedSource;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
codeDict.Add(reference, encodedSource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string EncodeSource(string source)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(source))
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(source);
|
|
||||||
|
|
||||||
using (var memoryStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress))
|
|
||||||
{
|
|
||||||
gzipStream.Write(bytes, 0, bytes.Length);
|
|
||||||
}
|
|
||||||
return Convert.ToBase64String(memoryStream.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string DecodeSource(string source)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(source))
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] compressedBytes = Convert.FromBase64String(source);
|
|
||||||
|
|
||||||
using (var memoryStream = new MemoryStream(compressedBytes))
|
|
||||||
{
|
|
||||||
using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
|
|
||||||
{
|
|
||||||
using (var outputStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
gzipStream.CopyTo(outputStream);
|
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(outputStream.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,362 +0,0 @@
|
|||||||
namespace Slang;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Assets.Scripts.Objects;
|
|
||||||
using Assets.Scripts.Objects.Electrical;
|
|
||||||
using Assets.Scripts.Objects.Motherboards;
|
|
||||||
using Assets.Scripts.UI;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
class LineErrorData
|
|
||||||
{
|
|
||||||
public AsciiString SourceRef;
|
|
||||||
public uint IC10ErrorSource;
|
|
||||||
public string SlangErrorReference;
|
|
||||||
public Range SlangErrorSpan;
|
|
||||||
|
|
||||||
public LineErrorData(
|
|
||||||
AsciiString sourceRef,
|
|
||||||
uint ic10ErrorSource,
|
|
||||||
string slangErrorRef,
|
|
||||||
Range slangErrorSpan
|
|
||||||
)
|
|
||||||
{
|
|
||||||
this.SourceRef = sourceRef;
|
|
||||||
this.IC10ErrorSource = ic10ErrorSource;
|
|
||||||
this.SlangErrorReference = slangErrorRef;
|
|
||||||
this.SlangErrorSpan = slangErrorSpan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch]
|
|
||||||
public static class SlangPatches
|
|
||||||
{
|
|
||||||
private static ProgrammableChipMotherboard? _currentlyEditingMotherboard;
|
|
||||||
private static AsciiString? _motherboardCachedCode;
|
|
||||||
private static Guid? _currentlyEditingGuid;
|
|
||||||
|
|
||||||
private static ConditionalWeakTable<ProgrammableChip, LineErrorData> _errorReferenceTable =
|
|
||||||
new();
|
|
||||||
|
|
||||||
[HarmonyPatch(
|
|
||||||
typeof(ProgrammableChipMotherboard),
|
|
||||||
nameof(ProgrammableChipMotherboard.InputFinished)
|
|
||||||
)]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void pgmb_InputFinished(ref string result)
|
|
||||||
{
|
|
||||||
_currentlyEditingMotherboard = null;
|
|
||||||
_motherboardCachedCode = null;
|
|
||||||
// guard to ensure we have valid IC10 before continuing
|
|
||||||
if (
|
|
||||||
!SlangPlugin.IsSlangSource(ref result)
|
|
||||||
|| !Marshal.CompileFromString(result, out var compiled, out var sourceMap)
|
|
||||||
|| string.IsNullOrEmpty(compiled)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var thisRef = _currentlyEditingGuid ?? Guid.NewGuid();
|
|
||||||
|
|
||||||
// Ensure we cache this compiled code for later retreival.
|
|
||||||
GlobalCode.SetSource(thisRef, result);
|
|
||||||
GlobalCode.SetSourceMap(thisRef, sourceMap);
|
|
||||||
|
|
||||||
_currentlyEditingGuid = null;
|
|
||||||
|
|
||||||
// Append REF to the bottom
|
|
||||||
compiled += $"\n{GlobalCode.SLANG_REF}{thisRef}";
|
|
||||||
result = compiled;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(ProgrammableChipMotherboard), nameof(ProgrammableChipMotherboard.OnEdit))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void isc_OnEdit(ProgrammableChipMotherboard __instance)
|
|
||||||
{
|
|
||||||
_currentlyEditingMotherboard = __instance;
|
|
||||||
_motherboardCachedCode = __instance.GetSourceCode();
|
|
||||||
var sourceCode = System.Text.Encoding.UTF8.GetString(
|
|
||||||
System.Text.Encoding.ASCII.GetBytes(__instance.GetSourceCode())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(sourceCode))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for REF at the bottom
|
|
||||||
var tagIndex = sourceCode.LastIndexOf(GlobalCode.SLANG_REF);
|
|
||||||
|
|
||||||
if (tagIndex == -1)
|
|
||||||
{
|
|
||||||
// this is not slang managed code
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!Guid.TryParse(
|
|
||||||
sourceCode.Substring(tagIndex + GlobalCode.SLANG_REF.Length).Trim(),
|
|
||||||
out Guid sourceRef
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// not a valid Guid, not managed by slang
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentlyEditingGuid = sourceRef;
|
|
||||||
var slangSource = GlobalCode.GetSource(sourceRef);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(slangSource))
|
|
||||||
{
|
|
||||||
// Didn't find that source ref in the global code manager.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
__instance.SetSourceCode(slangSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void HandleSerialization(ref string sourceCode)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(sourceCode))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if the file ends with the Reference Tag
|
|
||||||
var tagIndex = sourceCode.LastIndexOf(GlobalCode.SLANG_REF);
|
|
||||||
|
|
||||||
if (tagIndex == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string guidString = sourceCode.Substring(tagIndex + GlobalCode.SLANG_REF.Length).Trim();
|
|
||||||
|
|
||||||
if (!Guid.TryParse(guidString, out Guid slangRefGuid))
|
|
||||||
{
|
|
||||||
L.Warning($"Found SLANG_REF but failed to parse GUID: {guidString}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var slangEncoded = GlobalCode.GetEncoded(slangRefGuid);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(slangEncoded))
|
|
||||||
{
|
|
||||||
L.Warning(
|
|
||||||
$"Could not find encoded source for ref {slangRefGuid}. Save will contain compiled IC10 only."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the clean IC10 code (everything before the tag)
|
|
||||||
var cleanIc10 = sourceCode.Substring(0, tagIndex).TrimEnd();
|
|
||||||
|
|
||||||
// Append the encoded source tag to the bottom
|
|
||||||
sourceCode = $"{cleanIc10}\n{GlobalCode.SLANG_SRC}{slangEncoded}";
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(ProgrammableChip), nameof(ProgrammableChip.SerializeSave))]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void pgc_SerializeSave(ProgrammableChip __instance, ref ThingSaveData __result)
|
|
||||||
{
|
|
||||||
if (__result is not ProgrammableChipSaveData chipData)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string code = chipData.SourceCode;
|
|
||||||
HandleSerialization(ref code);
|
|
||||||
chipData.SourceCode = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(
|
|
||||||
typeof(ProgrammableChip),
|
|
||||||
nameof(ProgrammableChip.ErrorLineNumberString),
|
|
||||||
MethodType.Getter
|
|
||||||
)]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void pgc_ErrorLineNumberString(ProgrammableChip __instance, ref string __result)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
String.IsNullOrEmpty(__result)
|
|
||||||
|| !uint.TryParse(__result.Trim(), out var ic10ErrorLineNumber)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceAscii = __instance.GetSourceCode();
|
|
||||||
|
|
||||||
if (_errorReferenceTable.TryGetValue(__instance, out var cache))
|
|
||||||
{
|
|
||||||
if (cache.SourceRef.Equals(sourceAscii) && cache.IC10ErrorSource == ic10ErrorLineNumber)
|
|
||||||
{
|
|
||||||
__result = cache.SlangErrorReference;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var source = System.Text.Encoding.UTF8.GetString(
|
|
||||||
System.Text.Encoding.ASCII.GetBytes(__instance.GetSourceCode())
|
|
||||||
);
|
|
||||||
|
|
||||||
var slangIndex = source.LastIndexOf(GlobalCode.SLANG_REF);
|
|
||||||
|
|
||||||
if (
|
|
||||||
slangIndex < 0
|
|
||||||
|| !Guid.TryParse(
|
|
||||||
source
|
|
||||||
.Substring(
|
|
||||||
source.LastIndexOf(GlobalCode.SLANG_REF) + GlobalCode.SLANG_REF.Length
|
|
||||||
)
|
|
||||||
.Trim(),
|
|
||||||
out var slangGuid
|
|
||||||
)
|
|
||||||
|| !GlobalCode.GetSlangErrorLineFromICError(
|
|
||||||
slangGuid,
|
|
||||||
ic10ErrorLineNumber,
|
|
||||||
out var slangErrorLineNumber,
|
|
||||||
out var slangSpan
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Warning($"IC error at: {__result} -- Slang source error line: {slangErrorLineNumber}");
|
|
||||||
__result = slangErrorLineNumber.ToString();
|
|
||||||
_errorReferenceTable.Remove(__instance);
|
|
||||||
_errorReferenceTable.Add(
|
|
||||||
__instance,
|
|
||||||
new LineErrorData(
|
|
||||||
sourceAscii,
|
|
||||||
ic10ErrorLineNumber,
|
|
||||||
slangErrorLineNumber.ToString(),
|
|
||||||
slangSpan
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(
|
|
||||||
typeof(ProgrammableChip),
|
|
||||||
nameof(ProgrammableChip.SetSourceCode),
|
|
||||||
new Type[] { typeof(string) }
|
|
||||||
)]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void pgc_SetSourceCode_string(ProgrammableChip __instance, string sourceCode)
|
|
||||||
{
|
|
||||||
_errorReferenceTable.Remove(__instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(
|
|
||||||
typeof(ProgrammableChip),
|
|
||||||
nameof(ProgrammableChip.SetSourceCode),
|
|
||||||
new Type[] { typeof(string), typeof(ICircuitHolder) }
|
|
||||||
)]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void pgc_SetSourceCode_string_parent(
|
|
||||||
ProgrammableChip __instance,
|
|
||||||
string sourceCode,
|
|
||||||
ICircuitHolder parent
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_errorReferenceTable.Remove(__instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(
|
|
||||||
typeof(ProgrammableChipMotherboard),
|
|
||||||
nameof(ProgrammableChipMotherboard.SerializeSave)
|
|
||||||
)]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void pgmb_SerializeSave(
|
|
||||||
ProgrammableChipMotherboard __instance,
|
|
||||||
ref ThingSaveData __result
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (__result is not ProgrammableChipMotherboardSaveData chipData)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string code = chipData.SourceCode;
|
|
||||||
HandleSerialization(ref code);
|
|
||||||
chipData.SourceCode = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void HandleDeserialization(ref string sourceCode)
|
|
||||||
{
|
|
||||||
// Safety check for null/empty code
|
|
||||||
if (string.IsNullOrEmpty(sourceCode))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check for the #SLANG_SRC: footer
|
|
||||||
int tagIndex = sourceCode.LastIndexOf(GlobalCode.SLANG_SRC);
|
|
||||||
|
|
||||||
// If the tag is missing, this is just a normal IC10 script. Do nothing.
|
|
||||||
if (tagIndex == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Extract the Encoded Source (Base64)
|
|
||||||
string encodedSource = sourceCode.Substring(tagIndex + GlobalCode.SLANG_SRC.Length).Trim();
|
|
||||||
|
|
||||||
// Extract the IC10 Code (strip off the tag and the newline before it)
|
|
||||||
string ic10Code = sourceCode.Substring(0, tagIndex).TrimEnd();
|
|
||||||
|
|
||||||
// Generate a new Runtime GUID for this session
|
|
||||||
Guid runtimeGuid = Guid.NewGuid();
|
|
||||||
|
|
||||||
// Hydrate the Cache
|
|
||||||
GlobalCode.SetEncoded(runtimeGuid, encodedSource);
|
|
||||||
|
|
||||||
// Rewrite the SourceCode to the "Runtime" format (REF at bottom)
|
|
||||||
sourceCode = $"{ic10Code}\n{GlobalCode.SLANG_REF}{runtimeGuid}";
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(ProgrammableChip), nameof(ProgrammableChip.DeserializeSave))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void pgc_DeserializeSave(ref ThingSaveData savedData)
|
|
||||||
{
|
|
||||||
if (savedData is not ProgrammableChipSaveData pcSaveData)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string code = pcSaveData.SourceCode;
|
|
||||||
HandleDeserialization(ref code);
|
|
||||||
pcSaveData.SourceCode = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(
|
|
||||||
typeof(ProgrammableChipMotherboard),
|
|
||||||
nameof(ProgrammableChipMotherboard.DeserializeSave)
|
|
||||||
)]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void pgmb_DeserializeSave(ref ThingSaveData savedData)
|
|
||||||
{
|
|
||||||
if (savedData is not ProgrammableChipMotherboardSaveData pcSaveData)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string code = pcSaveData.SourceCode;
|
|
||||||
HandleDeserialization(ref code);
|
|
||||||
pcSaveData.SourceCode = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(InputSourceCode), nameof(InputSourceCode.ButtonInputCancel))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void isc_ButtonInputCancel()
|
|
||||||
{
|
|
||||||
if (_currentlyEditingMotherboard is null || _motherboardCachedCode is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentlyEditingMotherboard.SetSourceCode(_motherboardCachedCode);
|
|
||||||
|
|
||||||
_currentlyEditingMotherboard = null;
|
|
||||||
_motherboardCachedCode = null;
|
|
||||||
_currentlyEditingGuid = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Stationpedia), nameof(Stationpedia.Regenerate))]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void Stationpedia_Regenerate()
|
|
||||||
{
|
|
||||||
foreach (var page in Marshal.GetSlangDocs())
|
|
||||||
{
|
|
||||||
Stationpedia.Register(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using BepInEx;
|
using BepInEx;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using LaunchPadBooster;
|
|
||||||
|
|
||||||
namespace Slang
|
namespace Slang
|
||||||
{
|
{
|
||||||
@@ -41,9 +40,7 @@ namespace Slang
|
|||||||
{
|
{
|
||||||
public const string PluginGuid = "com.biddydev.slang";
|
public const string PluginGuid = "com.biddydev.slang";
|
||||||
public const string PluginName = "Slang";
|
public const string PluginName = "Slang";
|
||||||
public const string PluginVersion = "0.4.1";
|
public const string PluginVersion = "0.4.3";
|
||||||
|
|
||||||
public static Mod MOD = new Mod(PluginName, PluginVersion);
|
|
||||||
|
|
||||||
private Harmony? _harmony;
|
private Harmony? _harmony;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AssemblyName>StationeersSlang</AssemblyName>
|
<AssemblyName>StationeersSlang</AssemblyName>
|
||||||
<Description>Slang Compiler Bridge</Description>
|
<Description>Slang Compiler Bridge</Description>
|
||||||
<Version>0.4.0</Version>
|
<Version>0.4.2</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
2
rust_compiler/Cargo.lock
generated
2
rust_compiler/Cargo.lock
generated
@@ -930,7 +930,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slang"
|
name = "slang"
|
||||||
version = "0.4.0"
|
version = "0.4.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "slang"
|
name = "slang"
|
||||||
version = "0.4.1"
|
version = "0.4.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::compile;
|
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user