populate GlobalCode.sourceMaps

This commit is contained in:
2025-12-11 14:06:54 -07:00
parent 92f0d22805
commit 3edf0324c7
6 changed files with 113 additions and 21 deletions

View File

@@ -113,6 +113,34 @@ public static unsafe class SlangExtensions
return toReturn; return toReturn;
} }
public static unsafe List<SourceMapEntry> ToList(this Vec_FfiSourceMapEntry_t vec)
{
var toReturn = new List<SourceMapEntry>((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) private static uint GetColorForKind(uint kind)
{ {
switch (kind) switch (kind)

View File

@@ -71,18 +71,6 @@ public unsafe struct Vec_uint8_t {
public UIntPtr cap; public UIntPtr cap;
} }
public unsafe partial class Ffi {
/// <summary>
/// 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 <c>GetBytes()</c> call on a string in C#.
/// </summary>
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
Vec_uint8_t compile_from_string (
slice_ref_uint16_t input);
}
[StructLayout(LayoutKind.Sequential, Size = 16)] [StructLayout(LayoutKind.Sequential, Size = 16)]
public unsafe struct FfiRange_t { public unsafe struct FfiRange_t {
public UInt32 start_col; public UInt32 start_col;
@@ -94,6 +82,44 @@ public unsafe struct FfiRange_t {
public UInt32 end_line; public UInt32 end_line;
} }
[StructLayout(LayoutKind.Sequential, Size = 20)]
public unsafe struct FfiSourceMapEntry_t {
public UInt32 line_number;
public FfiRange_t span;
}
/// <summary>
/// Same as [<c>Vec<T></c>][<c>rust::Vec</c>], but with guaranteed <c>#[repr(C)]</c> layout
/// </summary>
[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 {
/// <summary>
/// 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 <c>GetBytes()</c> call on a string in C#.
/// </summary>
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
FfiCompilationResult_t compile_from_string (
slice_ref_uint16_t input);
}
[StructLayout(LayoutKind.Sequential, Size = 48)] [StructLayout(LayoutKind.Sequential, Size = 48)]
public unsafe struct FfiDiagnostic_t { public unsafe struct FfiDiagnostic_t {
public Vec_uint8_t message; public Vec_uint8_t message;
@@ -146,6 +172,12 @@ public unsafe partial class Ffi {
Vec_FfiDocumentedItem_t v); 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 { public unsafe partial class Ffi {
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
void free_ffi_diagnostic_vec ( void free_ffi_diagnostic_vec (

View File

@@ -15,11 +15,29 @@ public static class GlobalCode
// so that save file data is smaller // so that save file data is smaller
private static Dictionary<Guid, string> codeDict = new(); private static Dictionary<Guid, string> codeDict = new();
private static Dictionary<Guid, Dictionary<uint, List<Range>>> sourceMaps = new();
public static void ClearCache() public static void ClearCache()
{ {
codeDict.Clear(); codeDict.Clear();
} }
public static void SetSourceMap(Guid reference, List<SourceMapEntry> sourceMapEntries)
{
var builtDictionary = new Dictionary<uint, List<Range>>();
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) public static string GetSource(Guid reference)
{ {
if (!codeDict.ContainsKey(reference)) if (!codeDict.ContainsKey(reference))

View File

@@ -23,6 +23,12 @@ public struct Diagnostic
public Range Range; public Range Range;
} }
public struct SourceMapEntry
{
public Range SlangSource;
public uint Ic10Line;
}
public static class Marshal public static class Marshal
{ {
private static IntPtr _libraryHandle = IntPtr.Zero; 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<SourceMapEntry> sourceMapEntries
)
{ {
if (String.IsNullOrEmpty(inputString) || !EnsureLibLoaded()) if (String.IsNullOrEmpty(inputString) || !EnsureLibLoaded())
{ {
compiledString = String.Empty; compiledString = String.Empty;
sourceMapEntries = new();
return false; return false;
} }
@@ -95,19 +106,16 @@ public static class Marshal
}; };
var result = Ffi.compile_from_string(input); var result = Ffi.compile_from_string(input);
try try
{ {
if ((ulong)result.len < 1) sourceMapEntries = result.source_map.ToList();
{ compiledString = result.output_code.AsString();
compiledString = String.Empty;
return false;
}
compiledString = result.AsString();
return true; return true;
} }
finally finally
{ {
result.Drop(); Ffi.free_ffi_compilation_result(result);
} }
} }
} }

View File

@@ -26,7 +26,7 @@ public static class SlangPatches
// guard to ensure we have valid IC10 before continuing // guard to ensure we have valid IC10 before continuing
if ( if (
!SlangPlugin.IsSlangSource(ref result) !SlangPlugin.IsSlangSource(ref result)
|| !Marshal.CompileFromString(result, out string compiled) || !Marshal.CompileFromString(result, out var compiled, out var sourceMap)
|| string.IsNullOrEmpty(compiled) || string.IsNullOrEmpty(compiled)
) )
{ {
@@ -37,6 +37,7 @@ public static class SlangPatches
// Ensure we cache this compiled code for later retreival. // Ensure we cache this compiled code for later retreival.
GlobalCode.SetSource(thisRef, result); GlobalCode.SetSource(thisRef, result);
GlobalCode.SetSourceMap(thisRef, sourceMap);
_currentlyEditingGuid = null; _currentlyEditingGuid = null;

View File

@@ -94,6 +94,11 @@ impl From<lsp_types::Diagnostic> for FfiDiagnostic {
} }
} }
#[ffi_export]
pub fn free_ffi_compilation_result(input: FfiCompilationResult) {
drop(input)
}
#[ffi_export] #[ffi_export]
pub fn free_ffi_token_vec(v: safer_ffi::Vec<FfiToken>) { pub fn free_ffi_token_vec(v: safer_ffi::Vec<FfiToken>) {
drop(v) drop(v)