Skip to content

Generate XML documentation for .NET API #3368

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1277,14 +1277,18 @@ if (TD_ENABLE_DOTNET)
add_dependencies(tddotnet generate_dotnet_api)
endif()

target_compile_options(tddotnet PRIVATE "/doc")
if (CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set_target_properties(tddotnet PROPERTIES VS_WINRT_COMPONENT "true")
target_compile_options(tddotnet PUBLIC "/ZW")
else()
set_target_properties(tddotnet PROPERTIES COMPILE_FLAGS "/GR /clr")
target_compile_options(tddotnet PUBLIC "/EHa")
endif()

add_custom_command(TARGET tddotnet POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${TL_TD_AUTO_INCLUDE_DIR}/td/telegram/Telegram.Td.xml
$<TARGET_FILE_DIR:tddotnet>)
endif()

# tdc - TDLib interface in pure C
Expand Down
2 changes: 1 addition & 1 deletion td/generate/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ if (NOT CMAKE_CROSSCOMPILING)

if (TD_ENABLE_DOTNET)
if (PHP_EXECUTABLE)
set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TD_API_TLO_FILE} && ${PHP_EXECUTABLE} ../DotnetTlDocumentationGenerator.php ../scheme/td_api.tl td/telegram/TdDotNetApi.h)
set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TD_API_TLO_FILE} && ${PHP_EXECUTABLE} ../DotnetTlDocumentationGenerator.php ../scheme/td_api.tl td/telegram/Telegram.Td.xml)
else()
set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TD_API_TLO_FILE})
endif()
Expand Down
222 changes: 157 additions & 65 deletions td/generate/DotnetTlDocumentationGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,96 @@

class DotnetTlDocumentationGenerator extends TlDocumentationGenerator
{
protected function isStandaloneFile()
{
return true;
}

protected function getDocumentationBegin()
{
return <<<EOT
<?xml version="1.0"?>
<doc>
<assembly>
"Telegram.Td"
</assembly>
<members>
<member name="M:Telegram.Td.Client.SetLogMessageCallback(System.Int32,Telegram.Td.LogMessageCallback)">
<summary>
Sets the callback that will be called when a message is added to the internal TDLib log.
None of the TDLib methods can be called from the callback.
</summary>
<param name="max_verbosity_level">The maximum verbosity level of messages for which the callback will be called.</param>
<param name="callback">Callback that will be called when a message is added to the internal TDLib log.
Pass null to remove the callback.</param>
</member>
<member name="M:Telegram.Td.Client.Create(Telegram.Td.ClientResultHandler)">
<summary>
Creates new Client.
</summary>
<param name="updateHandler">Handler for incoming updates.</param>
<returns>Returns created Client.</returns>
</member>
<member name="M:Telegram.Td.Client.Run">
<summary>
Launches a cycle which will fetch all results of queries to TDLib and incoming updates from TDLib.
Must be called once on a separate dedicated thread on which all updates and query results from all Clients will be handled.
Never returns.
</summary>
</member>
<member name="M:Telegram.Td.Client.Execute(Telegram.Td.Api.Function)">
<summary>
Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously.
</summary>
<param name="function">Object representing a query to the TDLib.</param>
<returns>Returns request result.</returns>
<exception cref="T:System.NullReferenceException">Thrown when query is null.</exception>
</member>
<member name="M:Telegram.Td.Client.Send(Telegram.Td.Api.Function,Telegram.Td.ClientResultHandler)">
<summary>
Sends a request to the TDLib.
</summary>
<param name="function">Object representing a query to the TDLib.</param>
<param name="handler">Result handler with OnResult method which will be called with result
of the query or with Telegram.Td.Api.Error as parameter. If it is null, nothing will be called.</param>
<exception cref="T:System.NullReferenceException">Thrown when query is null.</exception>
</member>
<member name="T:Telegram.Td.Client">
<summary>
Main class for interaction with the TDLib.
</summary>
</member>
<member name="M:Telegram.Td.ClientResultHandler.OnResult(Telegram.Td.Api.Object)">
<summary>
Callback called on result of query to TDLib or incoming update from TDLib.
</summary>
<param name="object">Result of query or update of type Telegram.Td.Api.Update about new events.</param>
</member>
<member name="T:Telegram.Td.ClientResultHandler">
<summary>
Interface for handler for results of queries to TDLib and incoming updates from TDLib.
</summary>
</member>
<member name="T:Telegram.Td.LogMessageCallback">
<summary>
A type of callback function that will be called when a message is added to the internal TDLib log.
</summary>
<param name="verbosityLevel">Log verbosity level with which the message was added from -1 up to 1024.
If 0, then TDLib will crash as soon as the callback returns.
None of the TDLib methods can be called from the callback.</param>
<param name="message">The message added to the log.</param>
</member>
EOT;
}

protected function getDocumentationEnd()
{
return <<<EOT
</members>
</doc>
EOT;
}

protected function escapeDocumentation($doc)
{
$doc = preg_replace_callback('/(?<!["A-Za-z_\/])[A-Za-z]*_[A-Za-z_]*/',
Expand Down Expand Up @@ -43,18 +133,18 @@ protected function getTypeName($type)
{
switch ($type) {
case 'Bool':
return 'bool';
return 'System.Boolean';
case 'int32':
return 'int32';
return 'System.Int32';
case 'int53':
case 'int64':
return 'int64';
return 'System.Int64';
case 'double':
return 'float64';
return 'System.Double';
case 'string':
return 'String^';
return 'System.String';
case 'bytes':
return 'Array<BYTE>^';
return 'Windows.Foundation.Collections.IVector`1{System.Byte}';
case 'bool':
case 'int':
case 'long':
Expand All @@ -74,14 +164,14 @@ protected function getTypeName($type)
$this->printError("Wrong vector subtype in $type");
return '';
}
return 'Array<'.$this->getTypeName(substr($type, 7, -1)).'>^';
return 'System.Collections.Generic.IList{'.$this->getTypeName(substr($type, 7, -1)).'}';
}

if (preg_match('/[^A-Za-z0-9.]/', $type)) {
$this->printError("Wrong type $type");
return '';
}
return $this->getClassName($type).'^';
return $this->getClassName($type);
}
}

Expand Down Expand Up @@ -124,112 +214,114 @@ protected function fixLine($line)

protected function addGlobalDocumentation()
{
$this->addDocumentation('public interface class Object : BaseObject {', <<<EOT
/// <summary>
/// This class is a base class for all TDLib interface classes.
/// </summary>
EOT
);

$this->addDocumentation(' virtual String^ ToString() override;', <<<EOT
/// <summary>
/// Returns string representation of the object.
/// </summary>
/// <returns>Returns string representation of the object.</returns>
$this->addDocumentation('T:Object', <<<EOT
<member name="T:Telegram.Td.Api.Object">
<summary>
This class is a base class for all TDLib interface classes.
</summary>
</member>
EOT
);

$this->addDocumentation('public interface class Function : BaseObject {', <<<EOT
/// <summary>
/// This class is a base class for all TDLib interface function-classes.
/// </summary>
$this->addDocumentation('T:Function', <<<EOT
<member name="T:Telegram.Td.Api.Function">
<summary>
This class is a base class for all TDLib interface function-classes.
</summary>
</member>
EOT
);
}

protected function addAbstractClassDocumentation($class_name, $documentation)
{
$this->addDocumentation("public interface class $class_name : Object {", <<<EOT
/// <summary>
/// This class is an abstract base class.
/// $documentation
/// </summary>
$this->addDocumentation("T:$class_name", <<<EOT
<member name="T:Telegram.Td.Api.$class_name">
<summary>
This class is an abstract base class.
$documentation
</summary>
</member>
EOT
);

}

protected function getFunctionReturnTypeDescription($return_type, $for_constructor)
{
$shift = $for_constructor ? ' ' : '';
return "\r\n$shift/// <para>Returns <see cref=\"".substr($return_type, 0, -1).'"/>.</para>';
return "\r\n <para>Returns <see cref=\"T:Telegram.Td.Api.$return_type\"/>.</para>";
}

protected function addClassDocumentation($class_name, $base_class_name, $return_type, $description)
{
$this->addDocumentation("public ref class $class_name sealed : $base_class_name {", <<<EOT
/// <summary>
/// $description
/// </summary>
$this->addDocumentation("T:$class_name", <<<EOT
<member name="T:Telegram.Td.Api.$class_name">
<summary>
$description
</summary>
</member>
EOT
);
}

protected function addFieldDocumentation($class_name, $field_name, $type_name, $field_info, $may_be_null)
{
$end = ';';
if ($type_name == $field_name.'^' || ($type_name == 'Message^' && $field_name == 'ReplyToMessage')) {
$type_name = '::Telegram::Td::Api::'.$type_name;
$end = ' {';
} else if ($class_name == "WebPage" && $field_name == "Stickers" && $type_name == "Array<Sticker^>^") {
$type_name = 'Array<::Telegram::Td::Api::Sticker^>^';
$end = ' {';
}
$full_line = $class_name." property $type_name $field_name$end";
$this->addDocumentation($full_line, <<<EOT
/// <summary>
/// $field_info
/// </summary>
$this->addDocumentation("P:$class_name.$field_name", <<<EOT
<member name="P:Telegram.Td.Api.$class_name.$field_name">
<summary>
$field_info
</summary>
</member>
EOT
);
}

protected function addDefaultConstructorDocumentation($class_name, $class_description)
{
$this->addDocumentation(" $class_name();", <<<EOT
/// <summary>
/// $class_description
/// </summary>
$this->addDocumentation("M:$class_name.#ctor", <<<EOT
<member name="M:Telegram.Td.Api.$class_name.ToString">
<summary>
Returns string representation of the object.
</summary>
<returns>Returns string representation of the object.</returns>
</member>
<member name="M:Telegram.Td.Api.$class_name.#ctor">
<summary>
$class_description
</summary>
</member>
EOT
);
}

protected function addFullConstructorDocumentation($class_name, $class_description, $known_fields, $info)
{
$full_constructor = " $class_name(";
$full_constructor = "";
$colon = '';
foreach ($known_fields as $name => $type) {
$field_type = $this->getTypeName($type);
$pos = 0;
while (substr($field_type, $pos, 6) === 'Array<') {
$pos += 6;
while (substr($field_type, $pos, 35) === 'System.Collections.Generic.IList`1{') {
$pos += 35;
}
if (substr($field_type, $pos, 4) !== 'BYTE' && substr($field_type, $pos, 6) !== 'String' && ucfirst(substr($field_type, $pos)) === substr($field_type, $pos)) {
$field_type = substr($field_type, 0, $pos).'::Telegram::Td::Api::'.substr($field_type, $pos);
if (substr($field_type, $pos, 7) !== 'System.') {
$field_type = substr($field_type, 0, $pos).'Telegram.Td.Api.'.substr($field_type, $pos);
}
$full_constructor .= $colon.$field_type.' '.$this->getParameterName($name, $class_name);
$colon = ', ';
$full_constructor .= $colon.$field_type;
$colon = ',';
}
$full_constructor .= ');';

$full_doc = <<<EOT
/// <summary>
/// $class_description
/// </summary>
<member name="M:Telegram.Td.Api.$class_name.#ctor($full_constructor)">
<summary>
$class_description
</summary>
EOT;
foreach ($known_fields as $name => $type) {
$full_doc .= "\r\n /// <param name=\"".$this->getParameterName($name, $class_name).'">'.$info[$name]."</param>";
$full_doc .= "\r\n <param name=\"".$this->getParameterName($name, $class_name).'">'.$info[$name]."</param>";
}
$this->addDocumentation($full_constructor, $full_doc);
$full_doc .= "\r\n </member>";
$this->addDocumentation("M:$class_name.#ctor($full_constructor)", $full_doc);
}
}

Expand Down
27 changes: 27 additions & 0 deletions td/generate/TlDocumentationGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ final protected function addDot($str) {
return $str.'.';
}

protected function isStandaloneFile()
{
return false;
}

protected function getDocumentationBegin()
{
return "";
}

protected function getDocumentationEnd()
{
return "";
}

abstract protected function escapeDocumentation($doc);

abstract protected function getFieldName($name, $class_name);
Expand Down Expand Up @@ -310,6 +325,18 @@ public function generate($tl_scheme_file, $source_file)
}
}

if ($this->isStandaloneFile()) {
$result = $this->getDocumentationBegin()."\n";
foreach ($this->documentation as $value) {
$result .= $value."\n";
}
$result .= $this->getDocumentationEnd();
if (!file_exists($source_file) || file_get_contents($source_file) !== $result) {
file_put_contents($source_file, $result);
}
return;
}

$lines = file($source_file);
$result = '';
$current_class = '';
Expand Down