Skip to content

Get docu from cs func #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/DotNetLibraryBase/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,17 @@ def get_keyword_types(self, name: str) -> Optional[Mapping[str, Any]]:

return {i.Name: [self._convert_type(t) for t in i.Types] for i in keyword_info.Arguments}

def get_keyword_documentation(self, name: str) -> Optional[str]:
"""Get documentation for the given keyword."""
if self._library_info is None:
return None
try:
keyword_info = self._library_info.Keywords[name]
doc = keyword_info.Documentation
return str(doc) if doc is not None else None
except Exception:
return None

def run_keyword(self, name: str, args: Sequence[Any], kwargs: Mapping[str, Any]) -> Any:
method = getattr(self._instance, name)
real_args = list(args)
Expand Down
17 changes: 16 additions & 1 deletion src/RobotFramework.DotNetLibraryBase/LibraryInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace RobotFramework.DotNetLibraryBase;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Xml;
#pragma warning disable 1591

internal enum RobotLibraryConstants
{
Expand Down Expand Up @@ -44,7 +47,19 @@ public KeywordInfo(IEnumerable<MethodInfo> methods)
if (Methods.Length == 0)
throw new ArgumentException("At least one method is required", nameof(methods));

_documentation = new Lazy<string?>(() => null); // TODO: Get doc from attribute
_documentation = new Lazy<string?>(() => {
// Get XML documentation first
var xmlDoc = Methods[0].GetXmlDocumentation();

// Only if XML doc is not available, try attribute
if (string.IsNullOrEmpty(xmlDoc))
{
var docAttribute = Methods[0].GetCustomAttribute<RobotKeywordDocumentationAttribute>();
return docAttribute?.Documentation;
}

return xmlDoc;
});
_tags = new Lazy<string[]>(() => Array.Empty<string>()); // TODO: Get tags from attribute
_arguments = new Lazy<ArgumentInfo[]>(CollectArguments);
}
Expand Down
111 changes: 111 additions & 0 deletions src/RobotFramework.DotNetLibraryBase/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: 2024 Daniel Biehl <daniel.biehl@imbus.de>
//
// SPDX-License-Identifier: Apache-2.0

namespace RobotFramework.DotNetLibraryBase;

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using System.Text;

public static class ReflectionExtensions
{
public static string? GetXmlDocumentation(this MethodInfo methodInfo)
{
try
{
// Get the assembly that contains the method
var assembly = methodInfo.DeclaringType?.Assembly;
if (assembly == null) return null;

// Get the XML documentation file path
var xmlPath = Path.ChangeExtension(assembly.Location, "xml");
if (!File.Exists(xmlPath)) return null;

// Load and parse the XML documentation
var doc = XDocument.Load(xmlPath);

// Try to find the member with exact match first
var member = TryFindMember(doc, methodInfo, exact: true);

// If not found, try a more lenient search
member ??= TryFindMember(doc, methodInfo, exact: false);

if (member == null) return null;

// Return just the summary content
return member.Element("summary")?.Value.Trim();
}
catch
{
// If anything goes wrong, return null
return null;
}
}

private static XElement? TryFindMember(XDocument doc, MethodInfo methodInfo, bool exact)
{
var typeName = methodInfo.DeclaringType?.FullName ?? string.Empty;
var methodName = methodInfo.Name;

// Get all member elements
var members = doc.Root?.Elements("members")
.Elements("member")
.Where(m => m.Attribute("name")?.Value.StartsWith($"M:{typeName}.{methodName}") == true)
.ToList();

if (members == null || !members.Any())
return null;

if (exact)
{
// Try to find exact match with parameters
var parameters = methodInfo.GetParameters();
var parameterTypes = parameters.Length == 0
? string.Empty
: $"({string.Join(",", parameters.Select(GetTypeNameForXmlDoc))})";

var exactName = $"M:{typeName}.{methodName}{parameterTypes}";
return members.FirstOrDefault(m =>
string.Equals(m.Attribute("name")?.Value, exactName, StringComparison.Ordinal));
}

// Return first match (most specific one)
return members.FirstOrDefault();
}

private static string GetTypeNameForXmlDoc(ParameterInfo parameter)
{
return GetTypeNameForXmlDoc(parameter.ParameterType);
}

private static string GetTypeNameForXmlDoc(Type type)
{
// Handle array types
if (type.IsArray)
{
return $"{GetTypeNameForXmlDoc(type.GetElementType()!)}[]";
}

// Handle generic types
if (type.IsGenericType)
{
var name = type.Name.Split('`')[0];
var args = string.Join(",", type.GetGenericArguments().Select(t => GetTypeNameForXmlDoc(t)));
return $"{name}{{{args}}}";
}

// Handle nullable value types
var underlyingType = Nullable.GetUnderlyingType(type);
if (underlyingType != null)
{
return $"{GetTypeNameForXmlDoc(underlyingType)}?";
}

// Default case - use full name
return type.FullName ?? type.Name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
Expand Down
19 changes: 19 additions & 0 deletions src/RobotFramework.DotNetLibraryBase/RobotKeywordAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: 2024 Daniel Biehl <daniel.biehl@imbus.de>
//
// SPDX-License-Identifier: Apache-2.0

namespace RobotFramework.DotNetLibraryBase;

using System;

[AttributeUsage(AttributeTargets.Method)]
#pragma warning disable 1591
public class RobotKeywordDocumentationAttribute : Attribute
{
public string Documentation { get; }

public RobotKeywordDocumentationAttribute(string documentation)
{
Documentation = documentation;
}
}
60 changes: 60 additions & 0 deletions tests/DotNetDemoLibrary/DocumentationExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2024 Daniel Biehl <daniel.biehl@imbus.de>
//
// SPDX-License-Identifier: Apache-2.0

namespace DotNetDemoLibrary;

using System;

/// <summary>
/// Provides examples of using this class to see the docu
/// </summary>
public class DocumentationExample
{
/// <summary>
/// This is an example of XML documentation.
/// The documentation will be shown in Robot Framework.
/// </summary>
/// <remarks>
/// This keyword demonstrates XML documentation in IntelliSense.
/// </remarks>
public void XmlDocumentedKeyword()
{
Console.WriteLine("This keyword uses XML documentation");
}

/// <summary>
/// Example with parameters
/// </summary>
/// <param name="text">The text to display</param>
/// <param name="count">Number of times to repeat</param>
public void AttributeDocumentedKeyword(string text, int count = 1)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(text);
}
}

/// <summary>
/// This keyword has both XML documentation...
/// </summary>
/// <param name="message">Optional message to display</param>
/// <returns>The message that was displayed</returns>
public string BothDocumentationTypesKeyword(string message = "Default message")
{
Console.WriteLine(message);
return message;
}

/// <summary>
/// No Docu keyword
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public int adding(int a, int b)
{
return a + b;
}
}
5 changes: 5 additions & 0 deletions tests/DotNetDemoLibrary/DotNetDemoLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.0.1</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\RobotFramework.DotNetLibraryBase\RobotFramework.DotNetLibraryBase.csproj" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions tests/docu_test.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*** Settings ***
Library DotNetLibraryBase DotNetDemoLibrary.DocumentationExample, DotNetDemoLibrary

*** Test Cases ***
Test Documentation Types
XML Documented Keyword
Attribute Documented Keyword Hello from Robot count=3
${message}= Both Documentation Types Keyword Custom message
Log ${message}
${result}= Adding 5 5
Log ${result}