diff --git a/csharp_mod/Extensions.cs b/csharp_mod/Extensions.cs index cefd685..dfab846 100644 --- a/csharp_mod/Extensions.cs +++ b/csharp_mod/Extensions.cs @@ -113,6 +113,34 @@ public static unsafe class SlangExtensions return toReturn; } + public static unsafe List ToList(this Vec_FfiSourceMapEntry_t vec) + { + var toReturn = new List((int)vec.len); + + var currentPtr = vec.ptr; + + for (int i = 0; i < (int)vec.len; i++) + { + var item = currentPtr[i]; + + toReturn.Add( + new SourceMapEntry + { + Ic10Line = item.line_number, + SlangSource = new Range + { + EndCol = item.span.end_col, + EndLine = item.span.end_line, + StartCol = item.span.start_col, + StartLine = item.span.start_line, + }, + } + ); + } + + return toReturn; + } + private static uint GetColorForKind(uint kind) { switch (kind) diff --git a/csharp_mod/FfiGlue.cs b/csharp_mod/FfiGlue.cs index 3489eb4..c2eb521 100644 --- a/csharp_mod/FfiGlue.cs +++ b/csharp_mod/FfiGlue.cs @@ -71,18 +71,6 @@ public unsafe struct Vec_uint8_t { public UIntPtr cap; } -public unsafe partial class Ffi { - /// - /// C# handles strings as UTF16. We do NOT want to allocate that memory in C# because - /// we want to avoid GC. So we pass it to Rust to handle all the memory allocations. - /// This should result in the ability to compile many times without triggering frame drops - /// from the GC from a GetBytes() call on a string in C#. - /// - [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern - Vec_uint8_t compile_from_string ( - slice_ref_uint16_t input); -} - [StructLayout(LayoutKind.Sequential, Size = 16)] public unsafe struct FfiRange_t { public UInt32 start_col; @@ -94,6 +82,44 @@ public unsafe struct FfiRange_t { public UInt32 end_line; } +[StructLayout(LayoutKind.Sequential, Size = 20)] +public unsafe struct FfiSourceMapEntry_t { + public UInt32 line_number; + + public FfiRange_t span; +} + +/// +/// Same as [Vec][rust::Vec], but with guaranteed #[repr(C)] layout +/// +[StructLayout(LayoutKind.Sequential, Size = 24)] +public unsafe struct Vec_FfiSourceMapEntry_t { + public FfiSourceMapEntry_t * ptr; + + public UIntPtr len; + + public UIntPtr cap; +} + +[StructLayout(LayoutKind.Sequential, Size = 48)] +public unsafe struct FfiCompilationResult_t { + public Vec_uint8_t output_code; + + public Vec_FfiSourceMapEntry_t source_map; +} + +public unsafe partial class Ffi { + /// + /// C# handles strings as UTF16. We do NOT want to allocate that memory in C# because + /// we want to avoid GC. So we pass it to Rust to handle all the memory allocations. + /// This should result in the ability to compile many times without triggering frame drops + /// from the GC from a GetBytes() call on a string in C#. + /// + [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern + FfiCompilationResult_t compile_from_string ( + slice_ref_uint16_t input); +} + [StructLayout(LayoutKind.Sequential, Size = 48)] public unsafe struct FfiDiagnostic_t { public Vec_uint8_t message; @@ -146,6 +172,12 @@ public unsafe partial class Ffi { Vec_FfiDocumentedItem_t v); } +public unsafe partial class Ffi { + [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern + void free_ffi_compilation_result ( + FfiCompilationResult_t input); +} + public unsafe partial class Ffi { [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern void free_ffi_diagnostic_vec ( diff --git a/csharp_mod/GlobalCode.cs b/csharp_mod/GlobalCode.cs index 181a2f6..f472821 100644 --- a/csharp_mod/GlobalCode.cs +++ b/csharp_mod/GlobalCode.cs @@ -15,11 +15,29 @@ public static class GlobalCode // so that save file data is smaller private static Dictionary codeDict = new(); + private static Dictionary>> sourceMaps = new(); + public static void ClearCache() { codeDict.Clear(); } + public static void SetSourceMap(Guid reference, List sourceMapEntries) + { + var builtDictionary = new Dictionary>(); + + foreach (var entry in sourceMapEntries) + { + if (!builtDictionary.ContainsKey(entry.Ic10Line)) + { + builtDictionary[entry.Ic10Line] = new(); + } + builtDictionary[entry.Ic10Line].Add(entry.SlangSource); + } + + sourceMaps[reference] = builtDictionary; + } + public static string GetSource(Guid reference) { if (!codeDict.ContainsKey(reference)) diff --git a/csharp_mod/Marshal.cs b/csharp_mod/Marshal.cs index 1163b74..fdc12a6 100644 --- a/csharp_mod/Marshal.cs +++ b/csharp_mod/Marshal.cs @@ -23,6 +23,12 @@ public struct Diagnostic public Range Range; } +public struct SourceMapEntry +{ + public Range SlangSource; + public uint Ic10Line; +} + public static class Marshal { private static IntPtr _libraryHandle = IntPtr.Zero; @@ -78,11 +84,16 @@ public static class Marshal } } - public static unsafe bool CompileFromString(string inputString, out string compiledString) + public static unsafe bool CompileFromString( + string inputString, + out string compiledString, + out List sourceMapEntries + ) { if (String.IsNullOrEmpty(inputString) || !EnsureLibLoaded()) { compiledString = String.Empty; + sourceMapEntries = new(); return false; } @@ -95,19 +106,16 @@ public static class Marshal }; var result = Ffi.compile_from_string(input); + try { - if ((ulong)result.len < 1) - { - compiledString = String.Empty; - return false; - } - compiledString = result.AsString(); + sourceMapEntries = result.source_map.ToList(); + compiledString = result.output_code.AsString(); return true; } finally { - result.Drop(); + Ffi.free_ffi_compilation_result(result); } } } diff --git a/csharp_mod/Patches.cs b/csharp_mod/Patches.cs index 7f09926..d878f97 100644 --- a/csharp_mod/Patches.cs +++ b/csharp_mod/Patches.cs @@ -26,7 +26,7 @@ public static class SlangPatches // guard to ensure we have valid IC10 before continuing if ( !SlangPlugin.IsSlangSource(ref result) - || !Marshal.CompileFromString(result, out string compiled) + || !Marshal.CompileFromString(result, out var compiled, out var sourceMap) || string.IsNullOrEmpty(compiled) ) { @@ -37,6 +37,7 @@ public static class SlangPatches // Ensure we cache this compiled code for later retreival. GlobalCode.SetSource(thisRef, result); + GlobalCode.SetSourceMap(thisRef, sourceMap); _currentlyEditingGuid = null; diff --git a/rust_compiler/src/ffi/mod.rs b/rust_compiler/src/ffi/mod.rs index 6e04669..a213572 100644 --- a/rust_compiler/src/ffi/mod.rs +++ b/rust_compiler/src/ffi/mod.rs @@ -94,6 +94,11 @@ impl From for FfiDiagnostic { } } +#[ffi_export] +pub fn free_ffi_compilation_result(input: FfiCompilationResult) { + drop(input) +} + #[ffi_export] pub fn free_ffi_token_vec(v: safer_ffi::Vec) { drop(v)