Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
using System.Text;
using System.Xml;
namespace RobotNet10.ScriptEditor.Helpers.Monaco.Documentation;
public class DocumentationComment(
string summaryText = "",
DocumentationItem[]? typeParamElements = null,
DocumentationItem[]? paramElements = null,
string returnsText = "",
string remarksText = "",
string exampleText = "",
string valueText = "",
DocumentationItem[]? exception = null)
{
public string SummaryText { get; } = summaryText;
public DocumentationItem[] TypeParamElements { get; } = typeParamElements ?? [];
public DocumentationItem[] ParamElements { get; } = paramElements ?? [];
public string ReturnsText { get; } = returnsText;
public string RemarksText { get; } = remarksText;
public string ExampleText { get; } = exampleText;
public string ValueText { get; } = valueText;
public DocumentationItem[] Exception { get; } = exception ?? [];
public static DocumentationComment? From(string xmlDocumentation, string lineEnding)
{
if (string.IsNullOrEmpty(xmlDocumentation))
return Empty;
var reader = new StringReader("<docroot>" + xmlDocumentation + "</docroot>");
var summaryText = new StringBuilder();
var typeParamElements = new List<DocumentationItemBuilder>();
var paramElements = new List<DocumentationItemBuilder>();
var returnsText = new StringBuilder();
var remarksText = new StringBuilder();
var exampleText = new StringBuilder();
var valueText = new StringBuilder();
var exception = new List<DocumentationItemBuilder>();
using (var xml = XmlReader.Create(reader))
{
try
{
xml.Read();
string? elementName = null;
StringBuilder? currentSectionBuilder = null;
do
{
if (xml.NodeType == XmlNodeType.Element)
{
elementName = xml.Name.ToLowerInvariant();
switch (elementName)
{
case "filterpriority":
xml.Skip();
break;
case "remarks":
currentSectionBuilder = remarksText;
break;
case "example":
currentSectionBuilder = exampleText;
break;
case "exception":
DocumentationItemBuilder exceptionInstance = new(GetCref(xml["cref"]).TrimEnd());
currentSectionBuilder = exceptionInstance.Documentation;
exception.Add(exceptionInstance);
break;
case "returns":
currentSectionBuilder = returnsText;
break;
case "summary":
currentSectionBuilder = summaryText;
break;
case "see":
if (currentSectionBuilder is null) continue;
currentSectionBuilder.Append(GetCref(xml["cref"]));
currentSectionBuilder.Append(xml["langword"]);
break;
case "seealso":
if (currentSectionBuilder is null) continue;
currentSectionBuilder.Append("See also: ");
currentSectionBuilder.Append(GetCref(xml["cref"]));
break;
case "paramref":
if (currentSectionBuilder is null) continue;
currentSectionBuilder.Append(xml["name"]);
currentSectionBuilder.Append(' ');
break;
case "param":
DocumentationItemBuilder paramInstance = new(TrimMultiLineString(xml["name"] ?? "", lineEnding));
currentSectionBuilder = paramInstance.Documentation;
paramElements.Add(paramInstance);
break;
case "typeparamref":
if (currentSectionBuilder is null) continue;
currentSectionBuilder.Append(xml["name"]);
currentSectionBuilder.Append(' ');
break;
case "typeparam":
DocumentationItemBuilder typeParamInstance = new(TrimMultiLineString(xml["name"] ?? "", lineEnding));
currentSectionBuilder = typeParamInstance.Documentation;
typeParamElements.Add(typeParamInstance);
break;
case "value":
currentSectionBuilder = valueText;
break;
case "br":
case "para":
if (currentSectionBuilder is null) continue;
currentSectionBuilder.Append(lineEnding);
break;
}
}
else if (xml.NodeType == XmlNodeType.Text && currentSectionBuilder != null)
{
if (elementName == "code")
{
currentSectionBuilder.Append(xml.Value);
}
else
{
currentSectionBuilder.Append(TrimMultiLineString(xml.Value, lineEnding));
}
}
} while (xml.Read());
}
catch (Exception)
{
return null;
}
}
return new DocumentationComment(
summaryText.ToString(),
[.. typeParamElements.Select(s => s.ConvertToDocumentedObject())],
[.. paramElements.Select(s => s.ConvertToDocumentedObject())],
returnsText.ToString(),
remarksText.ToString(),
exampleText.ToString(),
valueText.ToString(),
[.. exception.Select(s => s.ConvertToDocumentedObject())]);
}
private static string TrimMultiLineString(string input, string lineEnding)
{
var lines = input.Split(separator, StringSplitOptions.RemoveEmptyEntries);
return string.Join(lineEnding, lines.Select(l => TrimStartRetainingSingleLeadingSpace(l)));
}
private static string GetCref(string? cref)
{
if (cref == null || cref.Trim().Length == 0)
{
return "";
}
if (cref.Length < 2)
{
return cref;
}
if (cref.Substring(1, 1) == ":")
{
return string.Concat(cref.AsSpan(2, cref.Length - 2), " ");
}
return cref + " ";
}
private static string TrimStartRetainingSingleLeadingSpace(string input)
{
if (string.IsNullOrWhiteSpace(input))
return string.Empty;
if (!char.IsWhiteSpace(input[0]))
return input;
return $" {input.TrimStart()}";
}
public string GetParameterText(string name)
=> Array.Find(ParamElements, parameter => parameter.Name == name)?.Documentation ?? string.Empty;
public string GetTypeParameterText(string name)
=> Array.Find(TypeParamElements, typeParam => typeParam.Name == name)?.Documentation ?? string.Empty;
public static readonly DocumentationComment Empty = new();
private static readonly string[] separator = ["\n", "\r\n"];
}
class DocumentationItemBuilder(string name)
{
public string Name { get; set; } = name;
public StringBuilder Documentation { get; set; } = new StringBuilder();
public DocumentationItem ConvertToDocumentedObject()
{
return new DocumentationItem(Name, Documentation.ToString());
}
}

View File

@@ -0,0 +1,7 @@
namespace RobotNet10.ScriptEditor.Helpers.Monaco.Documentation;
public class DocumentationItem(string name, string documentation)
{
public string Name { get; } = name;
public string Documentation { get; } = documentation;
}

View File

@@ -0,0 +1,171 @@
using Microsoft.CodeAnalysis;
using RobotNet10.ScriptEditor.Helpers.Monaco.Documentation;
using System.Text;
using System.Xml;
namespace RobotNet10.ScriptEditor.Helpers.Monaco;
public class DocumentationConverter
{/// <summary>
/// Converts the xml documentation string into a plain text string.
/// </summary>
public static string ConvertDocumentation(string xmlDocumentation, string lineEnding)
{
if (string.IsNullOrEmpty(xmlDocumentation))
return string.Empty;
var reader = new StringReader("<docroot>" + xmlDocumentation + "</docroot>");
using var xml = XmlReader.Create(reader);
var ret = new StringBuilder();
try
{
xml.Read();
string? elementName = null;
do
{
if (xml.NodeType == XmlNodeType.Element)
{
elementName = xml.Name.ToLowerInvariant();
switch (elementName)
{
case "filterpriority":
xml.Skip();
break;
case "remarks":
ret.Append(lineEnding);
ret.Append("Remarks:");
ret.Append(lineEnding);
break;
case "example":
ret.Append(lineEnding);
ret.Append("Example:");
ret.Append(lineEnding);
break;
case "exception":
ret.Append(lineEnding);
ret.Append(GetCref(xml["cref"]).TrimEnd());
ret.Append(": ");
break;
case "returns":
ret.Append(lineEnding);
ret.Append("Returns: ");
break;
case "see":
ret.Append(GetCref(xml["cref"]));
ret.Append(xml["langword"]);
break;
case "seealso":
ret.Append(lineEnding);
ret.Append("See also: ");
ret.Append(GetCref(xml["cref"]));
break;
case "paramref":
ret.Append(xml["name"]);
ret.Append(' ');
break;
case "typeparam":
ret.Append(lineEnding);
ret.Append('<');
ret.Append(TrimMultiLineString(xml["name"], lineEnding));
ret.Append(">: ");
break;
case "param":
ret.Append(lineEnding);
ret.Append(TrimMultiLineString(xml["name"], lineEnding));
ret.Append(": ");
break;
case "value":
ret.Append(lineEnding);
ret.Append("Value: ");
ret.Append(lineEnding);
break;
case "br":
case "para":
ret.Append(lineEnding);
break;
}
}
else if (xml.NodeType == XmlNodeType.Text)
{
if (elementName == "code")
{
ret.Append(xml.Value);
}
else
{
ret.Append(TrimMultiLineString(xml.Value, lineEnding));
}
}
} while (xml.Read());
}
catch (Exception)
{
return xmlDocumentation;
}
return ret.ToString();
}
private static readonly string[] separator = ["\n", "\r\n"];
private static string TrimMultiLineString(string? input, string lineEnding)
{
if (string.IsNullOrEmpty(input)) return "";
var lines = input.Split(separator, StringSplitOptions.RemoveEmptyEntries);
return string.Join(lineEnding, lines.Select(l => l.TrimStart()));
}
private static string GetCref(string? cref)
{
if (cref == null || cref.Trim().Length == 0)
{
return "";
}
if (cref.Length < 2)
{
return cref;
}
if (cref.Substring(1, 1) == ":")
{
return cref[2..] + " ";
}
return cref + " ";
}
public static DocumentationComment? GetStructuredDocumentation(string? xmlDocumentation, string lineEnding)
{
if (string.IsNullOrEmpty(xmlDocumentation)) return null;
return DocumentationComment.From(xmlDocumentation, lineEnding);
}
public static DocumentationComment? GetStructuredDocumentation(ISymbol symbol, string lineEnding = "\n")
{
return symbol switch
{
IParameterSymbol parameter => new DocumentationComment(summaryText: GetParameterDocumentation(parameter, lineEnding) ?? ""),
ITypeParameterSymbol typeParam => new DocumentationComment(summaryText: GetTypeParameterDocumentation(typeParam, lineEnding) ?? ""),
IAliasSymbol alias => new DocumentationComment(summaryText: GetAliasDocumentation(alias, lineEnding) ?? ""),
_ => GetStructuredDocumentation(symbol.GetDocumentationCommentXml(), lineEnding),
};
}
private static string? GetParameterDocumentation(IParameterSymbol parameter, string lineEnding = "\n")
{
var contaningSymbolDef = parameter.ContainingSymbol.OriginalDefinition;
return GetStructuredDocumentation(contaningSymbolDef.GetDocumentationCommentXml(), lineEnding)
?.GetParameterText(parameter.Name);
}
private static string? GetTypeParameterDocumentation(ITypeParameterSymbol typeParam, string lineEnding = "\n")
{
var contaningSymbol = typeParam.ContainingSymbol;
return GetStructuredDocumentation(contaningSymbol.GetDocumentationCommentXml(), lineEnding)
?.GetTypeParameterText(typeParam.Name);
}
private static string? GetAliasDocumentation(IAliasSymbol alias, string lineEnding = "\n")
{
return GetStructuredDocumentation(alias.Target.GetDocumentationCommentXml(), lineEnding)?.SummaryText;
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace RobotNet10.ScriptEditor.Helpers.Monaco;
public class InvocationContext
{
public SemanticModel SemanticModel { get; }
public int Position { get; }
public SyntaxNode Receiver { get; }
public IEnumerable<TypeInfo> ArgumentTypes { get; }
public IEnumerable<SyntaxToken> Separators { get; }
public bool IsInStaticContext { get; }
public InvocationContext(SemanticModel semModel, int position, SyntaxNode receiver, ArgumentListSyntax argList, bool isStatic)
{
SemanticModel = semModel;
Position = position;
Receiver = receiver;
ArgumentTypes = argList.Arguments.Select(argument => semModel.GetTypeInfo(argument.Expression));
Separators = argList.Arguments.GetSeparators();
IsInStaticContext = isStatic;
}
public InvocationContext(SemanticModel semModel, int position, SyntaxNode receiver, AttributeArgumentListSyntax argList, bool isStatic)
{
SemanticModel = semModel;
Position = position;
Receiver = receiver;
ArgumentTypes = argList.Arguments.Select(argument => semModel.GetTypeInfo(argument.Expression));
Separators = argList.Arguments.GetSeparators();
IsInStaticContext = isStatic;
}
}

View File

@@ -0,0 +1,9 @@
using BlazorMonaco;
namespace RobotNet10.ScriptEditor.Helpers.Monaco.Languages;
public class ParameterInformation
{
public string Label { get; set; } = "";
public MarkdownString? Documentation { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace RobotNet10.ScriptEditor.Helpers.Monaco.Languages;
public class SignatureHelp
{
public int ActiveParameter { get; set; }
public int ActiveSignature { get; set; }
public SignatureInformation[] Signatures { get; set; } = [];
}

View File

@@ -0,0 +1,6 @@
namespace RobotNet10.ScriptEditor.Helpers.Monaco.Languages;
public class SignatureHelpResult
{
public SignatureHelp Value { get; set; } = null!;
}

View File

@@ -0,0 +1,11 @@
using BlazorMonaco;
namespace RobotNet10.ScriptEditor.Helpers.Monaco.Languages;
public class SignatureInformation
{
public int? ActiveParameter { get; set; }
public MarkdownString? Documentation { get; set; }
public string Label { get; set; } = "";
public ParameterInformation[] Parameters { get; set; } = [];
}