Editor/HotCodeEditor/Navigation/XmlDocs.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
/// <summary>
/// Hands Roslyn the doc comments from a compiler-generated XML doc file (Foo.xml next to Foo.dll).
/// Roslyn's own XmlDocumentationProvider is in an assembly HotCode doesn't reference, and this is all it does.
/// The file is read on first use, not when the reference is created.
/// </summary>
public sealed class XmlDocs : DocumentationProvider
{
private readonly string _path;
private readonly string _text;
private Dictionary<string, string> _members;
private XmlDocs( string path, string text )
{
_path = path;
_text = text;
}
public static XmlDocs FromFile( string path ) => new( path, null );
public static XmlDocs FromText( string xml ) => new( null, xml );
protected override string GetDocumentationForSymbol( string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default )
{
var members = LazyInitializer.EnsureInitialized( ref _members, Load );
return members.TryGetValue( documentationMemberID, out var xml ) ? xml : "";
}
/// <summary>
/// Member id ("M:Sandbox.GameObject.Destroy") -> its <member> element as XML.
/// </summary>
private Dictionary<string, string> Load()
{
var members = new Dictionary<string, string>( StringComparer.Ordinal );
try
{
var doc = _text is not null ? XDocument.Parse( _text ) : XDocument.Load( _path );
foreach ( var member in doc.Descendants( "member" ) )
{
var name = (string)member.Attribute( "name" );
if ( !string.IsNullOrEmpty( name ) ) members[name] = member.ToString( SaveOptions.DisableFormatting );
}
}
catch ( Exception e ) when ( e is XmlException or IOException or UnauthorizedAccessException )
{
// No docs is fine; hints just show without them
}
return members;
}
public override bool Equals( object obj ) => obj is XmlDocs other && other._path == _path && ReferenceEquals( other._text, _text );
public override int GetHashCode() => HashCode.Combine( _path, _text );
}