You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-28 16:07:14 +00:00
GDScript LSP: Rework and extend BBCode to markdown docstring conversion
The original implementation was minimal and produced docstrings with poor formatting and no line returns in code editors other than VSCode. Co-authored-by: HolonProduction <holonproduction@gmail.com>
This commit is contained in:
committed by
HolonProduction
parent
ccf414ecb4
commit
cd2bd8f624
@@ -1927,28 +1927,67 @@ static String marked_documentation(const String &p_bbcode) {
|
||||
String markdown = p_bbcode.strip_edges();
|
||||
|
||||
Vector<String> lines = markdown.split("\n");
|
||||
bool in_code_block = false;
|
||||
int code_block_indent = -1;
|
||||
bool in_codeblock_tag = false;
|
||||
// This is for handling the special [codeblocks] syntax used by the built-in class reference.
|
||||
bool in_codeblocks_tag = false;
|
||||
bool in_codeblocks_gdscript_tag = false;
|
||||
|
||||
markdown = "";
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String line = lines[i];
|
||||
int block_start = line.find("[codeblock]");
|
||||
|
||||
// For [codeblocks] tags we locate a child [gdscript] tag and turn that
|
||||
// into a GDScript code listing. Other languages and the surrounding tag
|
||||
// are skipped.
|
||||
if (line.contains("[codeblocks]")) {
|
||||
in_codeblocks_tag = true;
|
||||
continue;
|
||||
}
|
||||
if (in_codeblocks_tag && line.contains("[/codeblocks]")) {
|
||||
in_codeblocks_tag = false;
|
||||
continue;
|
||||
}
|
||||
if (in_codeblocks_tag) {
|
||||
if (line.contains("[gdscript]")) {
|
||||
in_codeblocks_gdscript_tag = true;
|
||||
line = "```gdscript";
|
||||
} else if (in_codeblocks_gdscript_tag && line.contains("[/gdscript]")) {
|
||||
line = "```";
|
||||
in_codeblocks_gdscript_tag = false;
|
||||
} else if (!in_codeblocks_gdscript_tag) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to account for both [codeblock] and [codeblock lang=...].
|
||||
String codeblock_lang = "gdscript";
|
||||
int block_start = line.find("[codeblock");
|
||||
if (block_start != -1) {
|
||||
code_block_indent = block_start;
|
||||
in_code_block = true;
|
||||
line = "\n";
|
||||
} else if (in_code_block) {
|
||||
line = "\t" + line.substr(code_block_indent);
|
||||
int bracket_pos = line.find_char(']', block_start);
|
||||
if (bracket_pos != -1) {
|
||||
int lang_start = line.find("lang=", block_start);
|
||||
if (lang_start != -1 && lang_start < bracket_pos) {
|
||||
constexpr int LANG_PARAM_LENGTH = 5; // Length of "lang=".
|
||||
int lang_value_start = lang_start + LANG_PARAM_LENGTH;
|
||||
int lang_end = bracket_pos;
|
||||
if (lang_value_start < lang_end) {
|
||||
codeblock_lang = line.substr(lang_value_start, lang_end - lang_value_start);
|
||||
}
|
||||
}
|
||||
in_codeblock_tag = true;
|
||||
line = "```" + codeblock_lang;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_code_block && line.contains("[/codeblock]")) {
|
||||
line = "\n";
|
||||
in_code_block = false;
|
||||
if (in_codeblock_tag && line.contains("[/codeblock]")) {
|
||||
line = "```";
|
||||
in_codeblock_tag = false;
|
||||
}
|
||||
|
||||
if (!in_code_block) {
|
||||
if (!in_codeblock_tag) {
|
||||
line = line.strip_edges();
|
||||
line = line.replace("[br]", "\n\n");
|
||||
|
||||
line = line.replace("[code]", "`");
|
||||
line = line.replace("[/code]", "`");
|
||||
line = line.replace("[i]", "*");
|
||||
@@ -1957,17 +1996,126 @@ static String marked_documentation(const String &p_bbcode) {
|
||||
line = line.replace("[/b]", "**");
|
||||
line = line.replace("[u]", "__");
|
||||
line = line.replace("[/u]", "__");
|
||||
line = line.replace("[method ", "`");
|
||||
line = line.replace("[member ", "`");
|
||||
line = line.replace("[signal ", "`");
|
||||
line = line.replace("[enum ", "`");
|
||||
line = line.replace("[constant ", "`");
|
||||
line = line.replace_chars("[]", '`');
|
||||
line = line.replace("[s]", "~~");
|
||||
line = line.replace("[/s]", "~~");
|
||||
line = line.replace("[kbd]", "`");
|
||||
line = line.replace("[/kbd]", "`");
|
||||
line = line.replace("[center]", "");
|
||||
line = line.replace("[/center]", "");
|
||||
line = line.replace("[/font]", "");
|
||||
line = line.replace("[/color]", "");
|
||||
line = line.replace("[/img]", "");
|
||||
|
||||
// Convert remaining simple bracketed class names to backticks and literal brackets.
|
||||
// This handles cases like [Node2D], [Sprite2D], etc. and [lb] and [rb].
|
||||
int pos = 0;
|
||||
while ((pos = line.find_char('[', pos)) != -1) {
|
||||
// Replace the special cases for [lb] and [rb] first and walk
|
||||
// past them to avoid conflicts with class names.
|
||||
const bool is_within_bounds = pos + 4 <= line.length();
|
||||
if (is_within_bounds && line.substr(pos, 4) == "[lb]") {
|
||||
line = line.substr(0, pos) + "\\[" + line.substr(pos + 4);
|
||||
// We advance past the newly inserted `\\` and `[` characters (2 chars) so the
|
||||
// next `line.find()` does not stop at the same position.
|
||||
pos += 2;
|
||||
continue;
|
||||
} else if (is_within_bounds && line.substr(pos, 4) == "[rb]") {
|
||||
line = line.substr(0, pos) + "\\]" + line.substr(pos + 4);
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace class names in brackets.
|
||||
int end_pos = line.find_char(']', pos);
|
||||
if (end_pos == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
String content = line.substr(pos + 1, end_pos - pos - 1);
|
||||
// We only convert if it looks like a simple class name (no spaces, no special chars).
|
||||
// GDScript supports unicode characters as identifiers so we only exclude markers of other BBCode tags to avoid conflicts.
|
||||
bool is_class_name = (!content.is_empty() && content != "url" && !content.contains_char(' ') && !content.contains_char('=') && !content.contains_char('/'));
|
||||
if (is_class_name) {
|
||||
line = line.substr(0, pos) + "`" + content + "`" + line.substr(end_pos + 1);
|
||||
pos += content.length() + 2;
|
||||
} else {
|
||||
pos = end_pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int URL_OPEN_TAG_LENGTH = 5; // Length of "[url=".
|
||||
constexpr int URL_CLOSE_TAG_LENGTH = 6; // Length of "[/url]".
|
||||
|
||||
// This is for the case [url=$url]$text[/url].
|
||||
pos = 0;
|
||||
while ((pos = line.find("[url=", pos)) != -1) {
|
||||
int url_end = line.find_char(']', pos);
|
||||
int close_start = line.find("[/url]", url_end);
|
||||
if (url_end == -1 || close_start == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
String url = line.substr(pos + URL_OPEN_TAG_LENGTH, url_end - pos - URL_OPEN_TAG_LENGTH);
|
||||
String text = line.substr(url_end + 1, close_start - url_end - 1);
|
||||
String replacement = "[" + text + "](" + url + ")";
|
||||
line = line.substr(0, pos) + replacement + line.substr(close_start + URL_CLOSE_TAG_LENGTH);
|
||||
pos += replacement.length();
|
||||
}
|
||||
|
||||
// This is for the case [url]$url[/url].
|
||||
pos = 0;
|
||||
while ((pos = line.find("[url]", pos)) != -1) {
|
||||
int close_pos = line.find("[/url]", pos);
|
||||
if (close_pos == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
String url = line.substr(pos + URL_OPEN_TAG_LENGTH, close_pos - pos - URL_OPEN_TAG_LENGTH);
|
||||
String replacement = "[" + url + "](" + url + ")";
|
||||
line = line.substr(0, pos) + replacement + line.substr(close_pos + URL_CLOSE_TAG_LENGTH);
|
||||
pos += replacement.length();
|
||||
}
|
||||
|
||||
// Replace the various link types with inline code ([class MyNode] to `MyNode`).
|
||||
// Uses a while loop because there can occasionally be multiple links of the same type in a single line.
|
||||
const Vector<String> link_start_patterns = {
|
||||
"[class ", "[method ", "[member ", "[signal ", "[enum ", "[constant ",
|
||||
"[annotation ", "[constructor ", "[operator ", "[theme_item ", "[param "
|
||||
};
|
||||
for (const String &pattern : link_start_patterns) {
|
||||
int pattern_pos = 0;
|
||||
while ((pattern_pos = line.find(pattern, pattern_pos)) != -1) {
|
||||
int end_pos = line.find_char(']', pattern_pos);
|
||||
if (end_pos == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
String content = line.substr(pattern_pos + pattern.length(), end_pos - pattern_pos - pattern.length());
|
||||
String replacement = "`" + content + "`";
|
||||
line = line.substr(0, pattern_pos) + replacement + line.substr(end_pos + 1);
|
||||
pattern_pos += replacement.length();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove tags with attributes like [color=red], as they don't have a direct Markdown
|
||||
// equivalent supported by external tools.
|
||||
const String attribute_tags[] = {
|
||||
"color", "font", "img"
|
||||
};
|
||||
for (const String &tag_name : attribute_tags) {
|
||||
int tag_pos = 0;
|
||||
while ((tag_pos = line.find("[" + tag_name + "=", tag_pos)) != -1) {
|
||||
int end_pos = line.find_char(']', tag_pos);
|
||||
if (end_pos == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
line = line.substr(0, tag_pos) + line.substr(end_pos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_code_block && i < lines.size() - 1) {
|
||||
line += "\n\n";
|
||||
} else if (i < lines.size() - 1) {
|
||||
if (i < lines.size() - 1) {
|
||||
line += "\n";
|
||||
}
|
||||
markdown += line;
|
||||
|
||||
Reference in New Issue
Block a user