From a10b40f6fe1627aa8ebad995b05876b24e2bdc1f Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Tue, 2 Dec 2025 14:27:06 -0700 Subject: [PATCH] Implement changes from latest version of IC10Editor.dll --- csharp_mod/Formatter.cs | 110 ++++++++++++++++++----------- csharp_mod/Patches.cs | 1 - csharp_mod/stationeersSlang.csproj | 4 ++ 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/csharp_mod/Formatter.cs b/csharp_mod/Formatter.cs index a3dc844..2be094a 100644 --- a/csharp_mod/Formatter.cs +++ b/csharp_mod/Formatter.cs @@ -4,15 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using System.Timers; +using Cysharp.Threading.Tasks; using StationeersIC10Editor; public class SlangFormatter : ICodeFormatter { private CancellationTokenSource? _lspCancellationToken; - private readonly SynchronizationContext? _mainThreadContext; - private volatile bool IsDiagnosing = false; + private object _tokenLock = new(); public static readonly uint ColorInstruction = ColorFromHTML("#ffff00"); public static readonly uint ColorString = ColorFromHTML("#ce9178"); @@ -20,10 +19,39 @@ public class SlangFormatter : ICodeFormatter private HashSet _linesWithErrors = new(); public SlangFormatter() + : base() { - // 1. Capture the Main Thread context. - // This works because the Editor instantiates this class on the main thread. - _mainThreadContext = SynchronizationContext.Current; + OnCodeChanged += HandleCodeChanged; + } + + public static double MatchingScore(string input) + { + // Empty input is not valid Slang + if (string.IsNullOrWhiteSpace(input)) + return 0d; + + // Run the compiler to get diagnostics + var diagnostics = Marshal.DiagnoseSource(input); + + // Count the number of actual Errors (Severity 1). + // We ignore Warnings (2), Info (3), etc. + double errorCount = diagnostics.Count(d => d.Severity == 1); + + // Get the total line count to calculate error density + double lineCount = input.Split('\n').Length; + + // Prevent division by zero + if (lineCount == 0) + return 0d; + + // Calculate score: Start at 1.0 (100%) and subtract the ratio of errors per line. + // Example: 10 lines with 0 errors = 1.0 + // Example: 10 lines with 2 errors = 0.8 + // Example: 10 lines with 10+ errors = 0.0 + double score = 1.0d - (errorCount / lineCount); + + // Clamp the result between 0 and 1 + return Math.Max(0d, Math.Min(1d, score)); } public override string Compile() @@ -33,65 +61,65 @@ public class SlangFormatter : ICodeFormatter public override Line ParseLine(string line) { - HandleCodeChanged(); return Marshal.TokenizeLine(line); } private void HandleCodeChanged() { - if (IsDiagnosing) - return; + CancellationToken token; + string inputSrc; + lock (_tokenLock) + { + _lspCancellationToken?.Cancel(); + _lspCancellationToken = new CancellationTokenSource(); + token = _lspCancellationToken.Token; + inputSrc = this.RawText; + } - _lspCancellationToken?.Cancel(); - _lspCancellationToken?.Dispose(); - - _lspCancellationToken = new CancellationTokenSource(); - - _ = Task.Run(() => HandleLsp(_lspCancellationToken.Token), _lspCancellationToken.Token); + HandleLsp(inputSrc, token).Forget(); } private void OnTimerElapsed(object sender, ElapsedEventArgs e) { } - private async Task HandleLsp(CancellationToken cancellationToken) + private async UniTaskVoid HandleLsp(string inputSrc, CancellationToken cancellationToken) { try { - await Task.Delay(200, cancellationToken); + await UniTask.SwitchToThreadPool(); if (cancellationToken.IsCancellationRequested) - { return; - } - // 3. Dispatch the UI update to the Main Thread - if (_mainThreadContext != null) - { - // Post ensures ApplyDiagnostics runs on the captured thread (Main Thread) - _mainThreadContext.Post(_ => ApplyDiagnostics(), null); - } - else - { - // Fallback: If context is null (rare in Unity), try running directly - // but warn, as this might crash if not thread-safe. - L.Warning("SynchronizationContext was null. Attempting direct update (risky)."); - ApplyDiagnostics(); - } + await System.Threading.Tasks.Task.Delay(200, cancellationToken: cancellationToken); + + if (cancellationToken.IsCancellationRequested) + return; + + var dict = Marshal + .DiagnoseSource(inputSrc) + .GroupBy(d => d.Range.StartLine) + .ToDictionary(g => g.Key); + + if (cancellationToken.IsCancellationRequested) + return; + + await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken); + + ApplyDiagnostics(dict); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + L.Error(ex.Message); } - finally { } } // This runs on the Main Thread - private void ApplyDiagnostics() + private void ApplyDiagnostics(Dictionary> dict) { - List diagnosis = Marshal.DiagnoseSource(this.RawText); - - var dict = diagnosis.GroupBy(d => d.Range.StartLine).ToDictionary(g => g.Key); - var linesToRefresh = new HashSet(dict.Keys); linesToRefresh.UnionWith(_linesWithErrors); - IsDiagnosing = true; - foreach (var lineIndex in linesToRefresh) { // safety check for out of bounds (in case lines were deleted) @@ -134,7 +162,5 @@ public class SlangFormatter : ICodeFormatter } _linesWithErrors = new HashSet(dict.Keys); - - IsDiagnosing = false; } } diff --git a/csharp_mod/Patches.cs b/csharp_mod/Patches.cs index 22a4dd0..0222b50 100644 --- a/csharp_mod/Patches.cs +++ b/csharp_mod/Patches.cs @@ -214,7 +214,6 @@ public static class SlangPatches [HarmonyPrefix] public static void isc_ButtonInputCancel() { - L.Info("ButtonInputCancel called on the InputSourceCode static instance."); if (_currentlyEditingMotherboard is null || _motherboardCachedCode is null) { return; diff --git a/csharp_mod/stationeersSlang.csproj b/csharp_mod/stationeersSlang.csproj index d613b2e..e464914 100644 --- a/csharp_mod/stationeersSlang.csproj +++ b/csharp_mod/stationeersSlang.csproj @@ -41,6 +41,10 @@ $(ManagedDir)/Assembly-CSharp.dll False + + $(ManagedDir)/UniTask.dll + False + ./ref/IC10Editor.dll