diff --git a/Changelog.md b/Changelog.md index 87dc8b5..734956c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,14 @@ # Changelog +[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] - Update syscalls for `loadSlot` and `setSlot` to support expressions instead of diff --git a/ModData/About/About.xml b/ModData/About/About.xml index 2a468d3..a79e411 100644 --- a/ModData/About/About.xml +++ b/ModData/About/About.xml @@ -2,7 +2,7 @@ Slang JoeDiertay - 0.4.1 + 0.4.2 [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 Quality of Life - + Slang - High Level Language Compiler A modern programming experience for Stationeers. Write C-style code that compiles to IC10 instantly. diff --git a/csharp_mod/Formatter.cs b/csharp_mod/Formatter.cs index 486ca5f..16f81fc 100644 --- a/csharp_mod/Formatter.cs +++ b/csharp_mod/Formatter.cs @@ -5,13 +5,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using ImGuiNET; using StationeersIC10Editor; using StationeersIC10Editor.IC10; -using UnityEngine; public class SlangFormatter : ICodeFormatter { + public const string SLANG_SRC = "SLANG_SRC"; private CancellationTokenSource? _lspCancellationToken; private object _tokenLock = new(); @@ -48,6 +47,11 @@ public class SlangFormatter : ICodeFormatter if (string.IsNullOrWhiteSpace(input)) return 0d; + if (input.Contains(SLANG_SRC)) + { + return 1.0; + } + // Run the compiler to get diagnostics var diagnostics = Marshal.DiagnoseSource(input); @@ -74,7 +78,28 @@ public class SlangFormatter : ICodeFormatter 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 ResetCode(string code) + { + // for compatibility, we need to check for GlobalCode.SLANG_SRC + // `#SLANG_SRC:` + // and replace with `# SLANG_SRC: ` + if (code.Contains(GlobalCode.SLANG_SRC)) + { + code = code.Replace(GlobalCode.SLANG_SRC, $"# {SLANG_SRC}: "); + } + if (code.Contains(SLANG_SRC)) + { + code = ExtractEncodedSource(code, SLANG_SRC); + } + base.ResetCode(code); } public override StyledLine ParseLine(string line) diff --git a/csharp_mod/GlobalCode.cs b/csharp_mod/GlobalCode.cs index 786c5ce..bb00095 100644 --- a/csharp_mod/GlobalCode.cs +++ b/csharp_mod/GlobalCode.cs @@ -1,27 +1,27 @@ using System; using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; namespace Slang; public static class GlobalCode { - public const string SLANG_REF = "#SLANG_REF:"; - public const string SLANG_SRC = "SLANG_SRC"; + /// + /// 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. + /// + public const string SLANG_SRC = "#SLANG_SRC:"; - // This is a Dictionary of ENCODED source code, compressed - // so that save file data is smaller - private static Dictionary codeDict = new(); - - // This Dictionary stores the source maps for the given SLANG_REF, where - // 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 + /// 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>> sourceMaps = new(); - public static void ClearCache() - { - codeDict.Clear(); - } - public static void SetSourceMap(Guid reference, List sourceMapEntries) { var builtDictionary = new Dictionary>(); @@ -69,49 +69,4 @@ public static class GlobalCode slangSpan = foundRange[0]; 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) - { - return SlangFormatter.EncodeSource(source, SLANG_SRC); - } - - private static string DecodeSource(string source) - { - return SlangFormatter.DecodeSource(source, SLANG_SRC); - } } diff --git a/csharp_mod/Patches.cs b/csharp_mod/Patches.cs deleted file mode 100644 index 12fb8a2..0000000 --- a/csharp_mod/Patches.cs +++ /dev/null @@ -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 _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); - } - } -} diff --git a/csharp_mod/Plugin.cs b/csharp_mod/Plugin.cs index 84d3d35..37e50f1 100644 --- a/csharp_mod/Plugin.cs +++ b/csharp_mod/Plugin.cs @@ -41,7 +41,7 @@ namespace Slang { public const string PluginGuid = "com.biddydev.slang"; public const string PluginName = "Slang"; - public const string PluginVersion = "0.4.1"; + public const string PluginVersion = "0.4.2"; public static Mod MOD = new Mod(PluginName, PluginVersion); diff --git a/csharp_mod/stationeersSlang.csproj b/csharp_mod/stationeersSlang.csproj index e7aba92..f62a17e 100644 --- a/csharp_mod/stationeersSlang.csproj +++ b/csharp_mod/stationeersSlang.csproj @@ -5,7 +5,7 @@ enable StationeersSlang Slang Compiler Bridge - 0.4.0 + 0.4.2 true latest diff --git a/rust_compiler/Cargo.lock b/rust_compiler/Cargo.lock index 87e36d8..34712a0 100644 --- a/rust_compiler/Cargo.lock +++ b/rust_compiler/Cargo.lock @@ -930,7 +930,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slang" -version = "0.4.1" +version = "0.4.2" dependencies = [ "anyhow", "clap", diff --git a/rust_compiler/Cargo.toml b/rust_compiler/Cargo.toml index f6a1882..99ef668 100644 --- a/rust_compiler/Cargo.toml +++ b/rust_compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "slang" -version = "0.4.1" +version = "0.4.2" edition = "2021" [workspace]