Stationpedia docs

This commit is contained in:
2025-12-02 17:59:40 -07:00
parent bf1daf12cc
commit 75f1c5c44a
11 changed files with 211 additions and 24 deletions

View File

@@ -3,6 +3,7 @@ namespace Slang;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Assets.Scripts.UI;
using StationeersIC10Editor; using StationeersIC10Editor;
public static unsafe class SlangExtensions public static unsafe class SlangExtensions
@@ -133,4 +134,34 @@ public static unsafe class SlangExtensions
return SlangFormatter.ColorDefault; return SlangFormatter.ColorDefault;
} }
} }
public static unsafe List<StationpediaPage> ToList(this Vec_FfiDocumentedItem_t vec)
{
var toReturn = new List<StationpediaPage>((int)vec.len);
var currentPtr = vec.ptr;
for (int i = 0; i < (int)vec.len; i++)
{
var doc = currentPtr[i];
var docItemName = doc.item_name.AsString();
var formattedText = TextMeshProFormatter.FromMarkdown(doc.docs.AsString());
var pediaPage = new StationpediaPage(
$"slang-item-{docItemName}",
docItemName,
formattedText
);
pediaPage.Text = formattedText;
pediaPage.Description = formattedText;
pediaPage.ParsePage();
toReturn.Add(pediaPage);
}
Ffi.free_docs_vec(vec);
return toReturn;
}
} }

View File

@@ -121,6 +121,31 @@ public unsafe partial class Ffi {
slice_ref_uint16_t input); slice_ref_uint16_t input);
} }
[StructLayout(LayoutKind.Sequential, Size = 48)]
public unsafe struct FfiDocumentedItem_t {
public Vec_uint8_t item_name;
public Vec_uint8_t docs;
}
/// <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_FfiDocumentedItem_t {
public FfiDocumentedItem_t * ptr;
public UIntPtr len;
public UIntPtr cap;
}
public unsafe partial class Ffi {
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
void free_docs_vec (
Vec_FfiDocumentedItem_t v);
}
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 (
@@ -164,6 +189,11 @@ public unsafe partial class Ffi {
Vec_uint8_t s); Vec_uint8_t s);
} }
public unsafe partial class Ffi {
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
Vec_FfiDocumentedItem_t get_docs ();
}
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
Vec_FfiToken_t tokenize_line ( Vec_FfiToken_t tokenize_line (

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Assets.Scripts.UI;
using StationeersIC10Editor; using StationeersIC10Editor;
public struct Range public struct Range
@@ -151,6 +152,14 @@ public static class Marshal
} }
} }
/// <summary>
/// Gets the currently documented items from the Slang compiler and returns new StationpediaPages with correct formatting.
/// </summary>
public static unsafe List<StationpediaPage> GetSlangDocs()
{
return Ffi.get_docs().ToList();
}
private static string ExtractNativeLibrary(string libName) private static string ExtractNativeLibrary(string libName)
{ {
string destinationPath = Path.Combine(Path.GetTempPath(), libName); string destinationPath = Path.Combine(Path.GetTempPath(), libName);

View File

@@ -224,4 +224,14 @@ public static class SlangPatches
_currentlyEditingMotherboard = null; _currentlyEditingMotherboard = null;
_motherboardCachedCode = null; _motherboardCachedCode = null;
} }
[HarmonyPatch(typeof(Stationpedia), nameof(Stationpedia.Regenerate))]
[HarmonyPostfix]
public static void Stationpedia_Regenerate()
{
foreach (var page in Marshal.GetSlangDocs())
{
Stationpedia.Register(page);
}
}
} }

View File

@@ -1,22 +0,0 @@
using Assets.Scripts.UI;
namespace Slang
{
public static class SlangDocs
{
public static StationpediaPage[] Pages
{
get
{
return
[
new StationpediaPage(
"slang-init",
"Slang",
"Slang is a new high level language built specifically for Stationeers"
),
];
}
}
}
}

View File

@@ -0,0 +1,54 @@
using System.Text.RegularExpressions;
namespace Slang;
public static class TextMeshProFormatter
{
private const string CODE_COLOR = "#FFD700";
public static string FromMarkdown(string markdown)
{
if (string.IsNullOrEmpty(markdown))
return "";
// 1. Normalize Line Endings
string text = markdown.Replace("\r\n", "\n");
// 2. Handle Code Blocks (```)
text = Regex.Replace(
text,
@"```\s*(.*?)\s*```",
match =>
{
var codeContent = match.Groups[1].Value;
return $"<color={CODE_COLOR}>{codeContent}</color>"; // Gold color for code
},
RegexOptions.Singleline
);
// 3. Handle Headers (## Header)
// Convert ## Header to large bold text
text = Regex.Replace(
text,
@"^##(\s+)?(.+)$",
"<size=120%><b>$1</b></size>",
RegexOptions.Multiline
);
// 4. Handle Inline Code (`code`)
text = Regex.Replace(text, @"`([^`]+)`", $"<color={CODE_COLOR}>$1</color>");
// 5. Handle Bold (**text**)
text = Regex.Replace(text, @"\*\*(.+?)\*\*", "<b>$1</b>");
// 6. Handle Italics (*text*)
text = Regex.Replace(text, @"\*(.+?)\*", "<i>$1</i>");
// 7. Convert Newlines to TMP Line Breaks
// Stationpedia needs <br> or explicit newlines.
// Often just ensuring \n is preserved is enough, but <br> is safer for HTML-like parsers.
text = text.Replace("\n", "<br>");
return text;
}
}

View File

@@ -5,6 +5,8 @@ mod macros;
pub trait Documentation { pub trait Documentation {
/// Retreive documentation for this specific item. /// Retreive documentation for this specific item.
fn docs(&self) -> String; fn docs(&self) -> String;
fn get_all_documentation() -> Vec<(&'static str, String)>;
} }
pub mod prelude { pub mod prelude {

View File

@@ -55,7 +55,7 @@ macro_rules! documented {
)* )*
} }
// 2. Implement the Trait // 2. Implement the Documentation Trait
impl Documentation for $name { impl Documentation for $name {
fn docs(&self) -> String { fn docs(&self) -> String {
match self { match self {
@@ -79,6 +79,33 @@ macro_rules! documented {
)* )*
} }
} }
// 3. Implement Static Documentation Provider
#[allow(dead_code)]
fn get_all_documentation() -> Vec<(&'static str, String)> {
vec![
$(
(
stringify!($variant),
{
// Re-use the same extraction logic
let doc_lines: &[Option<&str>] = &[
$(
documented!(@doc_filter #[ $($variant_attr)* ])
),*
];
doc_lines.iter()
.filter_map(|&d| d)
.collect::<Vec<_>>()
.join("\n")
.trim()
.to_string()
}
)
),*
]
}
} }
}; };
} }

View File

@@ -236,6 +236,13 @@ impl Documentation for SysCall {
Self::Math(m) => m.docs(), Self::Math(m) => m.docs(),
} }
} }
fn get_all_documentation() -> Vec<(&'static str, String)> {
let mut all_docs = System::get_all_documentation();
all_docs.extend(Math::get_all_documentation());
all_docs
}
} }
impl std::fmt::Display for SysCall { impl std::fmt::Display for SysCall {

View File

@@ -95,6 +95,10 @@ impl Documentation for TokenType {
_ => "".into(), _ => "".into(),
} }
} }
fn get_all_documentation() -> Vec<(&'static str, String)> {
Keyword::get_all_documentation()
}
} }
impl From<TokenType> for u32 { impl From<TokenType> for u32 {

View File

@@ -1,6 +1,6 @@
use compiler::Compiler; use compiler::Compiler;
use helpers::Documentation; use helpers::Documentation;
use parser::Parser; use parser::{sys_call::SysCall, Parser};
use safer_ffi::prelude::*; use safer_ffi::prelude::*;
use std::io::BufWriter; use std::io::BufWriter;
use tokenizer::{ use tokenizer::{
@@ -27,6 +27,13 @@ pub struct FfiRange {
end_line: u32, end_line: u32,
} }
#[derive_ReprC]
#[repr(C)]
pub struct FfiDocumentedItem {
item_name: safer_ffi::String,
docs: safer_ffi::String,
}
impl From<lsp_types::Range> for FfiRange { impl From<lsp_types::Range> for FfiRange {
fn from(value: lsp_types::Range) -> Self { fn from(value: lsp_types::Range) -> Self {
Self { Self {
@@ -77,6 +84,11 @@ pub fn free_string(s: safer_ffi::String) {
drop(s) drop(s)
} }
#[ffi_export]
pub fn free_docs_vec(v: safer_ffi::Vec<FfiDocumentedItem>) {
drop(v)
}
/// C# handles strings as UTF16. We do NOT want to allocate that memory in C# because /// 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. /// 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 /// This should result in the ability to compile many times without triggering frame drops
@@ -184,3 +196,26 @@ pub fn diagnose_source(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<
res.unwrap_or(vec![].into()) res.unwrap_or(vec![].into())
} }
#[ffi_export]
pub fn get_docs() -> safer_ffi::Vec<FfiDocumentedItem> {
let res = std::panic::catch_unwind(|| {
let mut docs = SysCall::get_all_documentation();
docs.extend(TokenType::get_all_documentation());
docs
});
let Ok(result) = res else {
return vec![].into();
};
result
.into_iter()
.map(|(key, doc)| FfiDocumentedItem {
item_name: key.into(),
docs: doc.into(),
})
.collect::<Vec<_>>()
.into()
}