using Mesen.Interop; using Mesen.Localization; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; namespace Mesen.Debugger.Utilities { public class CodeCompletionHelper { private static Dictionary _documentation; private static Regex _linkRegex = new Regex("emu[.](([a-zA-Z0-9]+)(\\([a-zA-Z0-9 ,]*\\)){0,1})", RegexOptions.Compiled); static CodeCompletionHelper() { using StreamReader reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Mesen.Debugger.Documentation.LuaDocumentation.json")!); DocEntryViewModel[] documentation = (DocEntryViewModel[]?)JsonSerializer.Deserialize(reader.ReadToEnd(), typeof(DocEntryViewModel[]), MesenCamelCaseSerializerContext.Default) ?? Array.Empty(); _documentation = new Dictionary(); foreach(DocEntryViewModel entry in documentation) { _documentation[entry.Name] = entry; } } public static IEnumerable GetEntries() { return _documentation.Select(x => x.Key).OrderBy(x => x); } public static DocEntryViewModel? GetEntry(string keyword) { if(_documentation.TryGetValue(keyword, out DocEntryViewModel? entry)) { return entry; } return null; } public static string GenerateHtmlDocumentation() { string processText(string text) { return _linkRegex.Replace(text, (match) => "emu." + match.Groups[1].Value + "").Replace("\n", "
"); } StringBuilder sb = new(); sb.AppendLine(@$" Mesen Lua API Reference
"); DocEntryCategory? category = null; DocEntrySubcategory? subcategory = null; sb.AppendLine("
"); foreach(DocEntryViewModel entry in _documentation.Values.OrderBy(x => (int)x.Category).ThenBy(x => (int?)x.Subcategory)) { if(entry.Category != category) { if(category != null) { sb.AppendLine("
"); } if(subcategory != null) { sb.AppendLine("
"); } subcategory = null; sb.AppendLine($"{ResourceHelper.GetEnumText(entry.Category)}
"); category = entry.Category; } if(entry.Subcategory != subcategory && entry.Subcategory != null) { if(subcategory != null) { sb.AppendLine("
"); } sb.AppendLine($"{ResourceHelper.GetEnumText(entry.Subcategory)}
"); subcategory = entry.Subcategory; } sb.AppendLine($""); } if(category != null) { sb.AppendLine("
"); } if(subcategory != null) { sb.AppendLine(""); } sb.AppendLine(""); category = null; subcategory = null; sb.AppendLine("
"); sb.AppendLine("

Mesen Lua API reference

"); sb.AppendLine("

Important: This API is similar but not completely compatible with the old Mesen 0.9.x (or Mesen-S) Lua APIs.

"); sb.AppendLine("

Generated on " + EmuApi.GetMesenBuildDate() + " for Mesen " + EmuApi.GetMesenVersion().ToString() + ".

"); foreach(DocEntryViewModel entry in _documentation.Values.OrderBy(x => (int)x.Category).ThenBy(x => (int?)x.Subcategory)) { if(entry.Category != category) { sb.AppendLine($"

{ResourceHelper.GetEnumText(entry.Category)}

"); category = entry.Category; subcategory = null; } if(entry.Subcategory != subcategory && entry.Subcategory != null) { sb.AppendLine($"

{ResourceHelper.GetEnumText(entry.Subcategory)}

"); subcategory = entry.Subcategory; } sb.AppendLine($"

{entry.Name}

"); if(entry.EnumValues.Count > 0) { sb.AppendLine($"

Syntax

"); sb.AppendLine($"
emu.{entry.Name}.[value]
"); sb.AppendLine($"

Description

"); sb.AppendLine($"
{processText(entry.Description)}
"); sb.AppendLine($"

Values

"); sb.AppendLine($"
"); foreach(DocEnumValue value in entry.EnumValues) { sb.AppendLine($"
{value.Name} - {processText(value.Description)}
"); } sb.AppendLine($"
"); } else { sb.AppendLine($"

Syntax

"); sb.AppendLine($"
emu.{entry.Name}({string.Join(", ", entry.Parameters.Select(x => x.Name))})
"); sb.AppendLine($"

Parameters

"); if(entry.Parameters.Count == 0) { sb.AppendLine($"
<none>
"); } else { foreach(DocParam param in entry.Parameters) { sb.AppendLine($"
{param.Name} - "); if(param.Type.ToLowerInvariant() == "enum") { sb.AppendLine($"{param.CalculatedType}"); } else { sb.AppendLine($"{param.CalculatedType}"); } sb.AppendLine($"{(param.DefaultValue.Length > 0 ? $" (default: {param.DefaultValue})" : "")}"); sb.AppendLine($"
{processText(param.Description)}
"); sb.AppendLine($"
"); } } sb.AppendLine($"

Return value

"); sb.AppendLine($"
"); if(string.IsNullOrEmpty(entry.ReturnValue.Type)) { sb.AppendLine("<none>"); } else { sb.AppendLine($"{entry.ReturnValue.Type} - {processText(entry.ReturnValue.Description)}"); } sb.AppendLine($"
"); sb.AppendLine($"

Description

"); sb.AppendLine($"
{processText(entry.Description)}
"); } } sb.AppendLine("
"); sb.AppendLine(""); return sb.ToString(); } } public enum DocEntryCategory { Callbacks, Drawing, Emulation, Input, Logging, MemoryAccess, Miscellaneous, Enums } public enum DocEntrySubcategory { AccessCounters, Cdl, Cheats, SaveStates, Others } public class DocEntryViewModel { public string Name { get; set; } = ""; public string Description { get; set; } = ""; public DocEntryCategory Category { get; set; } = DocEntryCategory.Callbacks; public DocEntrySubcategory? Subcategory { get; set; } = null; public List Parameters { get; set; } = new(); public DocReturnValue ReturnValue { get; set; } = new(); public List EnumValues { get; set; } = new(); public string Syntax { get { StringBuilder sb = new(); sb.Append($"emu.{Name}("); bool isOptional = false; for(int i = 0; i < Parameters.Count; i++) { if(!string.IsNullOrEmpty(Parameters[i].DefaultValue)) { if(!isOptional) { isOptional = true; sb.Append("["); } } else { System.Diagnostics.Debug.Assert(!isOptional); } sb.Append(Parameters[i].Name); if(i < Parameters.Count - 1) { sb.Append(", "); } else { if(isOptional) { sb.Append("]"); } } } sb.Append(")"); return sb.ToString(); } } } public class DocParam { public string Name { get; set; } = ""; public string Type { get; set; } = ""; public string EnumName { get; set; } = ""; public string Description { get; set; } = ""; public string DefaultValue { get; set; } = ""; public string CalculatedType { get { if(Type.ToLowerInvariant() == "enum") { System.Diagnostics.Debug.Assert(EnumName.Length > 0); return $"Enum ({EnumName})"; } return Type; } } } public class DocReturnValue { public string Type { get; set; } = ""; public string Description { get; set; } = ""; } public class DocEnumValue { public string Name { get; set; } = ""; public string Description { get; set; } = ""; } }