Path: blob/master/modules/gdscript/language_server/godot_lsp.h
21100 views
/**************************************************************************/1/* godot_lsp.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132#include "core/doc_data.h"33#include "core/object/class_db.h"34#include "core/templates/list.h"3536// Enable additional LSP related logging.37//#define DEBUG_LSP3839#ifdef DEBUG_LSP40#define LOG_LSP(...) print_line("[ LSP -", __FILE__, ":", __LINE__, "-", __func__, "] -", ##__VA_ARGS__)41#else42#define LOG_LSP(...)43#endif4445namespace LSP {4647typedef String DocumentUri;4849/** Format BBCode documentation from DocData to markdown */50static String marked_documentation(const String &p_bbcode);5152/**53* Text documents are identified using a URI. On the protocol level, URIs are passed as strings.54*/55struct TextDocumentIdentifier {56/**57* The text document's URI.58*/59DocumentUri uri;6061_FORCE_INLINE_ void load(const Dictionary &p_params) {62uri = p_params["uri"];63}6465_FORCE_INLINE_ Dictionary to_json() const {66Dictionary dict;67dict["uri"] = uri;68return dict;69}70};7172/**73* Position in a text document expressed as zero-based line and zero-based character offset.74* A position is between two characters like an ‘insert’ cursor in a editor.75* Special values like for example -1 to denote the end of a line are not supported.76*/77struct Position {78/**79* Line position in a document (zero-based).80*/81int line = 0;8283/**84* Character offset on a line in a document (zero-based). Assuming that the line is85* represented as a string, the `character` value represents the gap between the86* `character` and `character + 1`.87*88* If the character value is greater than the line length it defaults back to the89* line length.90*/91int character = 0;9293_FORCE_INLINE_ bool operator==(const Position &p_other) const {94return line == p_other.line && character == p_other.character;95}9697String to_string() const {98return vformat("(%d,%d)", line, character);99}100101_FORCE_INLINE_ void load(const Dictionary &p_params) {102line = p_params["line"];103character = p_params["character"];104}105106_FORCE_INLINE_ Dictionary to_json() const {107Dictionary dict;108dict["line"] = line;109dict["character"] = character;110return dict;111}112};113114/**115* A range in a text document expressed as (zero-based) start and end positions.116* A range is comparable to a selection in an editor. Therefore the end position is exclusive.117* If you want to specify a range that contains a line including the line ending character(s) then use an end position denoting the start of the next line.118*/119struct Range {120/**121* The range's start position.122*/123Position start;124125/**126* The range's end position.127*/128Position end;129130_FORCE_INLINE_ bool operator==(const Range &p_other) const {131return start == p_other.start && end == p_other.end;132}133134bool contains(const Position &p_pos) const {135// Inside line range.136if (start.line <= p_pos.line && p_pos.line <= end.line) {137// If on start line: must come after start char.138bool start_ok = p_pos.line == start.line ? start.character <= p_pos.character : true;139// If on end line: must come before end char.140bool end_ok = p_pos.line == end.line ? p_pos.character <= end.character : true;141return start_ok && end_ok;142} else {143return false;144}145}146147String to_string() const {148return vformat("[%s:%s]", start.to_string(), end.to_string());149}150151_FORCE_INLINE_ void load(const Dictionary &p_params) {152start.load(p_params["start"]);153end.load(p_params["end"]);154}155156_FORCE_INLINE_ Dictionary to_json() const {157Dictionary dict;158dict["start"] = start.to_json();159dict["end"] = end.to_json();160return dict;161}162};163164/**165* Represents a location inside a resource, such as a line inside a text file.166*/167struct Location {168DocumentUri uri;169Range range;170171_FORCE_INLINE_ void load(const Dictionary &p_params) {172uri = p_params["uri"];173range.load(p_params["range"]);174}175176_FORCE_INLINE_ Dictionary to_json() const {177Dictionary dict;178dict["uri"] = uri;179dict["range"] = range.to_json();180return dict;181}182};183184/**185* Represents a link between a source and a target location.186*/187struct LocationLink {188/**189* Span of the origin of this link.190*191* Used as the underlined span for mouse interaction. Defaults to the word range at192* the mouse position.193*/194Range *originSelectionRange = nullptr;195196/**197* The target resource identifier of this link.198*/199String targetUri;200201/**202* The full target range of this link. If the target for example is a symbol then target range is the203* range enclosing this symbol not including leading/trailing whitespace but everything else204* like comments. This information is typically used to highlight the range in the editor.205*/206Range targetRange;207208/**209* The range that should be selected and revealed when this link is being followed, e.g the name of a function.210* Must be contained by the `targetRange`. See also `DocumentSymbol#range`211*/212Range targetSelectionRange;213};214215/**216* A parameter literal used in requests to pass a text document and a position inside that document.217*/218struct TextDocumentPositionParams {219/**220* The text document.221*/222TextDocumentIdentifier textDocument;223224/**225* The position inside the text document.226*/227Position position;228229_FORCE_INLINE_ void load(const Dictionary &p_params) {230textDocument.load(p_params["textDocument"]);231position.load(p_params["position"]);232}233234_FORCE_INLINE_ Dictionary to_json() const {235Dictionary dict;236dict["textDocument"] = textDocument.to_json();237dict["position"] = position.to_json();238return dict;239}240};241242struct ReferenceContext {243/**244* Include the declaration of the current symbol.245*/246bool includeDeclaration = false;247};248249struct ShowMessageParams {250/**251* The message type. See {@link MessageType}.252*/253int type;254255/**256* The actual message.257*/258String message;259260_FORCE_INLINE_ Dictionary to_json() const {261Dictionary dict;262dict["type"] = type;263dict["message"] = message;264return dict;265}266};267268struct ReferenceParams : TextDocumentPositionParams {269ReferenceContext context;270};271272struct DocumentLinkParams {273/**274* The document to provide document links for.275*/276TextDocumentIdentifier textDocument;277278_FORCE_INLINE_ void load(const Dictionary &p_params) {279textDocument.load(p_params["textDocument"]);280}281};282283/**284* A document link is a range in a text document that links to an internal or external resource, like another285* text document or a web site.286*/287struct DocumentLink {288/**289* The range this link applies to.290*/291Range range;292293/**294* The uri this link points to. If missing a resolve request is sent later.295*/296DocumentUri target;297298Dictionary to_json() const {299Dictionary dict;300dict["range"] = range.to_json();301dict["target"] = target;302return dict;303}304};305306/**307* A textual edit applicable to a text document.308*/309struct TextEdit {310/**311* The range of the text document to be manipulated. To insert312* text into a document create a range where start === end.313*/314Range range;315316/**317* The string to be inserted. For delete operations use an318* empty string.319*/320String newText;321};322323/**324* The edits to be applied.325*/326struct WorkspaceEdit {327/**328* Holds changes to existing resources.329*/330HashMap<String, Vector<TextEdit>> changes;331332_FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) {333if (changes.has(uri)) {334changes[uri].push_back(edit);335} else {336Vector<TextEdit> edits;337edits.push_back(edit);338changes[uri] = edits;339}340}341342_FORCE_INLINE_ Dictionary to_json() const {343Dictionary dict;344345Dictionary out_changes;346for (const KeyValue<String, Vector<TextEdit>> &E : changes) {347Array edits;348for (int i = 0; i < E.value.size(); ++i) {349Dictionary text_edit;350text_edit["range"] = E.value[i].range.to_json();351text_edit["newText"] = E.value[i].newText;352edits.push_back(text_edit);353}354out_changes[E.key] = edits;355}356dict["changes"] = out_changes;357358return dict;359}360361_FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) {362TextEdit new_edit;363new_edit.newText = new_text;364new_edit.range.start.line = line;365new_edit.range.start.character = start_character;366new_edit.range.end.line = line;367new_edit.range.end.character = end_character;368369if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) {370E->value.push_back(new_edit);371} else {372Vector<TextEdit> edit_list;373edit_list.push_back(new_edit);374changes.insert(uri, edit_list);375}376}377};378379/**380* Represents a reference to a command.381* Provides a title which will be used to represent a command in the UI.382* Commands are identified by a string identifier.383* The recommended way to handle commands is to implement their execution on the server side if the client and server provides the corresponding capabilities.384* Alternatively the tool extension code could handle the command. The protocol currently doesn’t specify a set of well-known commands.385*/386struct Command {387/**388* Title of the command, like `save`.389*/390String title;391/**392* The identifier of the actual command handler.393*/394String command;395/**396* Arguments that the command handler should be397* invoked with.398*/399Array arguments;400401Dictionary to_json() const {402Dictionary dict;403dict["title"] = title;404dict["command"] = command;405if (arguments.size()) {406dict["arguments"] = arguments;407}408return dict;409}410};411412// Use namespace instead of enumeration to follow the LSP specifications.413// `LSP::EnumName::EnumValue` is OK but `LSP::EnumValue` is not.414415namespace TextDocumentSyncKind {416/**417* Documents should not be synced at all.418*/419static const int None = 0;420421/**422* Documents are synced by always sending the full content423* of the document.424*/425static const int Full = 1;426427/**428* Documents are synced by sending the full content on open.429* After that only incremental updates to the document are430* send.431*/432static const int Incremental = 2;433}; // namespace TextDocumentSyncKind434435namespace MessageType {436/**437* An error message.438*/439static const int Error = 1;440/**441* A warning message.442*/443static const int Warning = 2;444/**445* An information message.446*/447static const int Info = 3;448/**449* A log message.450*/451static const int Log = 4;452}; // namespace MessageType453454/**455* Completion options.456*/457struct CompletionOptions {458/**459* The server provides support to resolve additional460* information for a completion item.461*/462bool resolveProvider = true;463464/**465* The characters that trigger completion automatically.466*/467Vector<String> triggerCharacters;468469CompletionOptions() {470triggerCharacters.push_back(".");471triggerCharacters.push_back("$");472triggerCharacters.push_back("'");473triggerCharacters.push_back("\"");474}475476Dictionary to_json() const {477Dictionary dict;478dict["resolveProvider"] = resolveProvider;479dict["triggerCharacters"] = triggerCharacters;480return dict;481}482};483484/**485* Signature help options.486*/487struct SignatureHelpOptions {488/**489* The characters that trigger signature help490* automatically.491*/492Vector<String> triggerCharacters;493494Dictionary to_json() {495Dictionary dict;496dict["triggerCharacters"] = triggerCharacters;497return dict;498}499};500501/**502* Code Lens options.503*/504struct CodeLensOptions {505/**506* Code lens has a resolve provider as well.507*/508bool resolveProvider = false;509510Dictionary to_json() {511Dictionary dict;512dict["resolveProvider"] = resolveProvider;513return dict;514}515};516517/**518* Rename options519*/520struct RenameOptions {521/**522* Renames should be checked and tested before being executed.523*/524bool prepareProvider = true;525526Dictionary to_json() {527Dictionary dict;528dict["prepareProvider"] = prepareProvider;529return dict;530}531};532533/**534* Document link options.535*/536struct DocumentLinkOptions {537/**538* Document links have a resolve provider as well.539*/540bool resolveProvider = false;541542Dictionary to_json() {543Dictionary dict;544dict["resolveProvider"] = resolveProvider;545return dict;546}547};548549/**550* Execute command options.551*/552struct ExecuteCommandOptions {553/**554* The commands to be executed on the server555*/556Vector<String> commands;557558Dictionary to_json() {559Dictionary dict;560dict["commands"] = commands;561return dict;562}563};564565/**566* Save options.567*/568struct SaveOptions {569/**570* The client is supposed to include the content on save.571*/572bool includeText = true;573574Dictionary to_json() {575Dictionary dict;576dict["includeText"] = includeText;577return dict;578}579};580581/**582* Color provider options.583*/584struct ColorProviderOptions {585Dictionary to_json() {586return Dictionary();587}588};589590/**591* Folding range provider options.592*/593struct FoldingRangeProviderOptions {594Dictionary to_json() {595return Dictionary();596}597};598599struct TextDocumentSyncOptions {600/**601* Open and close notifications are sent to the server. If omitted open close notification should not602* be sent.603*/604bool openClose = true;605606/**607* Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full608* and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None.609*/610int change = TextDocumentSyncKind::Full;611612/**613* If present will save notifications are sent to the server. If omitted the notification should not be614* sent.615*/616bool willSave = false;617618/**619* If present will save wait until requests are sent to the server. If omitted the request should not be620* sent.621*/622bool willSaveWaitUntil = true;623624/**625* If present save notifications are sent to the server. If omitted the notification should not be626* sent.627*/628SaveOptions save;629630Dictionary to_json() {631Dictionary dict;632dict["willSaveWaitUntil"] = willSaveWaitUntil;633dict["willSave"] = willSave;634dict["openClose"] = openClose;635dict["change"] = change;636dict["save"] = save.to_json();637return dict;638}639};640641/**642* Static registration options to be returned in the initialize request.643*/644struct StaticRegistrationOptions {645/**646* The id used to register the request. The id can be used to deregister647* the request again. See also Registration#id.648*/649String id;650};651652/**653* Format document on type options.654*/655struct DocumentOnTypeFormattingOptions {656/**657* A character on which formatting should be triggered, like `}`.658*/659String firstTriggerCharacter;660661/**662* More trigger characters.663*/664Vector<String> moreTriggerCharacter;665666Dictionary to_json() {667Dictionary dict;668dict["firstTriggerCharacter"] = firstTriggerCharacter;669dict["moreTriggerCharacter"] = moreTriggerCharacter;670return dict;671}672};673674enum class LanguageId {675GDSCRIPT,676OTHER,677};678679struct TextDocumentItem {680/**681* The text document's URI.682*/683DocumentUri uri;684685/**686* The text document's language identifier.687*/688LanguageId languageId;689690/**691* The version number of this document (it will increase after each692* change, including undo/redo).693*/694int version = 0;695696/**697* The content of the opened text document.698*/699String text;700701void load(const Dictionary &p_dict) {702uri = p_dict["uri"];703version = p_dict["version"];704text = p_dict["text"];705706// Clients should use "gdscript" as language id, but we can't enforce it.707// We normalize some known ids to make them easier to work with:708// Rider < 2026.1: "gd"709// Kate: "godot"710String rawLanguageId = p_dict["languageId"];711if (rawLanguageId == "gdscript" || rawLanguageId == "gd" || rawLanguageId == "godot") {712languageId = LanguageId::GDSCRIPT;713} else {714languageId = LanguageId::OTHER;715}716}717};718719/**720* An event describing a change to a text document.721*/722struct TextDocumentContentChangeEvent {723/**724* The new text of the range/document.725*/726String text;727728void load(const Dictionary &p_params) {729text = p_params["text"];730}731};732733// Use namespace instead of enumeration to follow the LSP specifications734namespace DiagnosticSeverity {735/**736* Reports an error.737*/738static const int Error = 1;739/**740* Reports a warning.741*/742static const int Warning = 2;743/**744* Reports an information.745*/746static const int Information = 3;747/**748* Reports a hint.749*/750static const int Hint = 4;751}; // namespace DiagnosticSeverity752753/**754* Represents a related message and source code location for a diagnostic. This should be755* used to point to code locations that cause or related to a diagnostics, e.g when duplicating756* a symbol in a scope.757*/758struct DiagnosticRelatedInformation {759/**760* The location of this related diagnostic information.761*/762Location location;763764/**765* The message of this related diagnostic information.766*/767String message;768769Dictionary to_json() const {770Dictionary dict;771dict["location"] = location.to_json();772dict["message"] = message;773return dict;774}775};776777/**778* Represents a diagnostic, such as a compiler error or warning.779* Diagnostic objects are only valid in the scope of a resource.780*/781struct Diagnostic {782/**783* The range at which the message applies.784*/785Range range;786787/**788* The diagnostic's severity. Can be omitted. If omitted it is up to the789* client to interpret diagnostics as error, warning, info or hint.790*/791int severity = 0;792793/**794* The diagnostic's code, which might appear in the user interface.795*/796int code = 0;797798/**799* A human-readable string describing the source of this800* diagnostic, e.g. 'typescript' or 'super lint'.801*/802String source;803804/**805* The diagnostic's message.806*/807String message;808809/**810* An array of related diagnostic information, e.g. when symbol-names within811* a scope collide all definitions can be marked via this property.812*/813Vector<DiagnosticRelatedInformation> relatedInformation;814815Dictionary to_json() const {816Dictionary dict;817dict["range"] = range.to_json();818dict["code"] = code;819dict["severity"] = severity;820dict["message"] = message;821dict["source"] = source;822if (!relatedInformation.is_empty()) {823Array arr;824arr.resize(relatedInformation.size());825for (int i = 0; i < relatedInformation.size(); i++) {826arr[i] = relatedInformation[i].to_json();827}828dict["relatedInformation"] = arr;829}830return dict;831}832};833834// Use namespace instead of enumeration to follow the LSP specifications835/**836* Describes the content type that a client supports in various837* result literals like `Hover`, `ParameterInfo` or `CompletionItem`.838*839* Please note that `MarkupKinds` must not start with a `$`. This kinds840* are reserved for internal usage.841*/842namespace MarkupKind {843static const String PlainText = "plaintext";844static const String Markdown = "markdown";845}; // namespace MarkupKind846847/**848* A `MarkupContent` literal represents a string value which content is interpreted base on its849* kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds.850*851* If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues.852* See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting853*854* Here is an example how such a string can be constructed using JavaScript / TypeScript:855* ```typescript856* let markdown: MarkdownContent = {857* kind: MarkupKind.Markdown,858* value: [859* '# Header',860* 'Some text',861* '```typescript',862* 'someCode();',863* '```'864* ].join('\n')865* };866* ```867*868* *Please Note* that clients might sanitize the return markdown. A client could decide to869* remove HTML from the markdown to avoid script execution.870*/871struct MarkupContent {872/**873* The type of the Markup.874*/875String kind;876877/**878* The content itself.879*/880String value;881882MarkupContent() {883kind = MarkupKind::Markdown;884}885886MarkupContent(const String &p_value) {887value = p_value;888kind = MarkupKind::Markdown;889}890891Dictionary to_json() const {892Dictionary dict;893dict["kind"] = kind;894dict["value"] = value;895return dict;896}897};898899// Use namespace instead of enumeration to follow the LSP specifications900// `LSP::EnumName::EnumValue` is OK but `LSP::EnumValue` is not.901// And here C++ compilers are unhappy with our enumeration name like `Color`, `File`, `RefCounted` etc.902/**903* The kind of a completion entry.904*/905namespace CompletionItemKind {906static const int Text = 1;907static const int Method = 2;908static const int Function = 3;909static const int Constructor = 4;910static const int Field = 5;911static const int Variable = 6;912static const int Class = 7;913static const int Interface = 8;914static const int Module = 9;915static const int Property = 10;916static const int Unit = 11;917static const int Value = 12;918static const int Enum = 13;919static const int Keyword = 14;920static const int Snippet = 15;921static const int Color = 16;922static const int File = 17;923static const int RefCounted = 18;924static const int Folder = 19;925static const int EnumMember = 20;926static const int Constant = 21;927static const int Struct = 22;928static const int Event = 23;929static const int Operator = 24;930static const int TypeParameter = 25;931}; // namespace CompletionItemKind932933// Use namespace instead of enumeration to follow the LSP specifications.934/**935* Defines whether the insert text in a completion item should be interpreted as936* plain text or a snippet.937*/938namespace InsertTextFormat {939/**940* The primary text to be inserted is treated as a plain string.941*/942static const int PlainText = 1;943944/**945* The primary text to be inserted is treated as a snippet.946*947* A snippet can define tab stops and placeholders with `$1`, `$2`948* and `${3:foo}`. `$0` defines the final tab stop, it defaults to949* the end of the snippet. Placeholders with equal identifiers are linked,950* that is typing in one will update others too.951*/952static const int Snippet = 2;953}; // namespace InsertTextFormat954955struct CompletionItem {956/**957* The label of this completion item. By default958* also the text that is inserted when selecting959* this completion.960*/961String label;962963/**964* The kind of this completion item. Based of the kind965* an icon is chosen by the editor. The standardized set966* of available values is defined in `CompletionItemKind`.967*/968int kind = 0;969970/**971* A human-readable string with additional information972* about this item, like type or symbol information.973*/974String detail;975976/**977* A human-readable string that represents a doc-comment.978*/979MarkupContent documentation;980981/**982* Indicates if this item is deprecated.983*/984bool deprecated = false;985986/**987* Select this item when showing.988*989* *Note* that only one completion item can be selected and that the990* tool / client decides which item that is. The rule is that the *first*991* item of those that match best is selected.992*/993bool preselect = false;994995/**996* A string that should be used when comparing this item997* with other items. When omitted the label is used998* as the filter text for this item.999*/1000String sortText;10011002/**1003* A string that should be used when filtering a set of1004* completion items. When omitted the label is used as the1005* filter text for this item.1006*/1007String filterText;10081009/**1010* A string that should be inserted into a document when selecting1011* this completion. When omitted the label is used as the insert text1012* for this item.1013*1014* The `insertText` is subject to interpretation by the client side.1015* Some tools might not take the string literally. For example1016* VS Code when code complete is requested in this example1017* `con<cursor position>` and a completion item with an `insertText` of1018* `console` is provided it will only insert `sole`. Therefore it is1019* recommended to use `textEdit` instead since it avoids additional client1020* side interpretation.1021*/1022String insertText;10231024/**1025* The format of the insert text. The format applies to both the `insertText` property1026* and the `newText` property of a provided `textEdit`.1027*/1028int insertTextFormat = 0;10291030/**1031* An edit which is applied to a document when selecting this completion. When an edit is provided the value of1032* `insertText` is ignored.1033*1034* *Note:* The range of the edit must be a single line range and it must contain the position at which completion1035* has been requested.1036*/1037TextEdit textEdit;10381039/**1040* An optional array of additional text edits that are applied when1041* selecting this completion. Edits must not overlap (including the same insert position)1042* with the main edit nor with themselves.1043*1044* Additional text edits should be used to change text unrelated to the current cursor position1045* (for example adding an import statement at the top of the file if the completion item will1046* insert an unqualified type).1047*/1048Vector<TextEdit> additionalTextEdits;10491050/**1051* An optional set of characters that when pressed while this completion is active will accept it first and1052* then type that character. *Note* that all commit characters should have `length=1` and that superfluous1053* characters will be ignored.1054*/1055Vector<String> commitCharacters;10561057/**1058* An optional command that is executed *after* inserting this completion. *Note* that1059* additional modifications to the current document should be described with the1060* additionalTextEdits-property.1061*/1062Command command;10631064/**1065* A data entry field that is preserved on a completion item between1066* a completion and a completion resolve request.1067*/1068Variant data;10691070_FORCE_INLINE_ Dictionary to_json(bool resolved = false) const {1071Dictionary dict;1072dict["label"] = label;1073dict["kind"] = kind;1074dict["data"] = data;1075if (!insertText.is_empty()) {1076dict["insertText"] = insertText;1077}1078if (resolved) {1079dict["detail"] = detail;1080dict["documentation"] = documentation.to_json();1081dict["deprecated"] = deprecated;1082dict["preselect"] = preselect;1083if (!sortText.is_empty()) {1084dict["sortText"] = sortText;1085}1086if (!filterText.is_empty()) {1087dict["filterText"] = filterText;1088}1089if (commitCharacters.size()) {1090dict["commitCharacters"] = commitCharacters;1091}1092if (!command.command.is_empty()) {1093dict["command"] = command.to_json();1094}1095}1096return dict;1097}10981099void load(const Dictionary &p_dict) {1100if (p_dict.has("label")) {1101label = p_dict["label"];1102}1103if (p_dict.has("kind")) {1104kind = p_dict["kind"];1105}1106if (p_dict.has("detail")) {1107detail = p_dict["detail"];1108}1109if (p_dict.has("documentation")) {1110Variant doc = p_dict["documentation"];1111if (doc.is_string()) {1112documentation.value = doc;1113} else if (doc.get_type() == Variant::DICTIONARY) {1114Dictionary v = doc;1115documentation.value = v["value"];1116}1117}1118if (p_dict.has("deprecated")) {1119deprecated = p_dict["deprecated"];1120}1121if (p_dict.has("preselect")) {1122preselect = p_dict["preselect"];1123}1124if (p_dict.has("sortText")) {1125sortText = p_dict["sortText"];1126}1127if (p_dict.has("filterText")) {1128filterText = p_dict["filterText"];1129}1130if (p_dict.has("insertText")) {1131insertText = p_dict["insertText"];1132}1133if (p_dict.has("data")) {1134data = p_dict["data"];1135}1136}1137};11381139/**1140* Represents a collection of [completion items](#CompletionItem) to be presented1141* in the editor.1142*/1143struct CompletionList {1144/**1145* This list it not complete. Further typing should result in recomputing1146* this list.1147*/1148bool isIncomplete = false;11491150/**1151* The completion items.1152*/1153Vector<CompletionItem> items;1154};11551156// Use namespace instead of enumeration to follow the LSP specifications1157// `LSP::EnumName::EnumValue` is OK but `LSP::EnumValue` is not1158// And here C++ compilers are unhappy with our enumeration name like `String`, `Array`, `Object` etc1159/**1160* A symbol kind.1161*/1162namespace SymbolKind {1163static const int File = 1;1164static const int Module = 2;1165static const int Namespace = 3;1166static const int Package = 4;1167static const int Class = 5;1168static const int Method = 6;1169static const int Property = 7;1170static const int Field = 8;1171static const int Constructor = 9;1172static const int Enum = 10;1173static const int Interface = 11;1174static const int Function = 12;1175static const int Variable = 13;1176static const int Constant = 14;1177static const int String = 15;1178static const int Number = 16;1179static const int Boolean = 17;1180static const int Array = 18;1181static const int Object = 19;1182static const int Key = 20;1183static const int Null = 21;1184static const int EnumMember = 22;1185static const int Struct = 23;1186static const int Event = 24;1187static const int Operator = 25;1188static const int TypeParameter = 26;1189}; // namespace SymbolKind11901191/**1192* Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be1193* hierarchical and they have two ranges: one that encloses its definition and one that points to its most interesting range,1194* e.g. the range of an identifier.1195*/1196struct DocumentSymbol {1197/**1198* The name of this symbol. Will be displayed in the user interface and therefore must not be1199* an empty string or a string only consisting of white spaces.1200*/1201String name;12021203/**1204* More detail for this symbol, e.g the signature of a function.1205*/1206String detail;12071208/**1209* Documentation for this symbol.1210*/1211String documentation;12121213/**1214* Class name for the native symbols.1215*/1216String native_class;12171218/**1219* The kind of this symbol.1220*/1221int kind = SymbolKind::File;12221223/**1224* Indicates if this symbol is deprecated.1225*/1226bool deprecated = false;12271228/**1229* If `true`: Symbol is local to script and cannot be accessed somewhere else.1230*1231* For example: local variable inside a `func`.1232*/1233bool local = false;12341235/**1236* The range enclosing this symbol not including leading/trailing whitespace but everything else1237* like comments. This information is typically used to determine if the clients cursor is1238* inside the symbol to reveal in the symbol in the UI.1239*/1240Range range;12411242/**1243* The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.1244* Must be contained by the `range`.1245*/1246Range selectionRange;12471248DocumentUri uri;1249String script_path;12501251/**1252* Children of this symbol, e.g. properties of a class.1253*/1254Vector<DocumentSymbol> children;12551256Dictionary to_json(bool with_doc = false) const {1257Dictionary dict;1258dict["name"] = name;1259dict["detail"] = detail;1260dict["kind"] = kind;1261dict["deprecated"] = deprecated;1262dict["range"] = range.to_json();1263dict["selectionRange"] = selectionRange.to_json();1264if (with_doc) {1265dict["documentation"] = documentation;1266dict["native_class"] = native_class;1267}1268if (!children.is_empty()) {1269Array arr;1270for (int i = 0; i < children.size(); i++) {1271if (children[i].local) {1272continue;1273}1274arr.push_back(children[i].to_json(with_doc));1275}1276if (!children.is_empty()) {1277dict["children"] = arr;1278}1279}1280return dict;1281}12821283_FORCE_INLINE_ MarkupContent render() const {1284MarkupContent markdown;1285if (detail.length()) {1286markdown.value = "\t" + detail + "\n\n";1287}1288if (documentation.length()) {1289markdown.value += marked_documentation(documentation) + "\n\n";1290}1291if (script_path.length()) {1292markdown.value += "Defined in [" + script_path + "](" + uri + ")";1293}1294return markdown;1295}12961297_FORCE_INLINE_ CompletionItem make_completion_item(bool resolved = false) const {1298LSP::CompletionItem item;1299item.label = name;13001301if (resolved) {1302item.documentation = render();1303}13041305switch (kind) {1306case LSP::SymbolKind::Enum:1307item.kind = LSP::CompletionItemKind::Enum;1308break;1309case LSP::SymbolKind::Class:1310item.kind = LSP::CompletionItemKind::Class;1311break;1312case LSP::SymbolKind::Property:1313item.kind = LSP::CompletionItemKind::Property;1314break;1315case LSP::SymbolKind::Method:1316case LSP::SymbolKind::Function:1317item.kind = LSP::CompletionItemKind::Method;1318break;1319case LSP::SymbolKind::Event:1320item.kind = LSP::CompletionItemKind::Event;1321break;1322case LSP::SymbolKind::Constant:1323item.kind = LSP::CompletionItemKind::Constant;1324break;1325case LSP::SymbolKind::Variable:1326item.kind = LSP::CompletionItemKind::Variable;1327break;1328case LSP::SymbolKind::File:1329item.kind = LSP::CompletionItemKind::File;1330break;1331default:1332item.kind = LSP::CompletionItemKind::Text;1333break;1334}13351336return item;1337}1338};13391340struct ApplyWorkspaceEditParams {1341WorkspaceEdit edit;13421343Dictionary to_json() {1344Dictionary dict;13451346dict["edit"] = edit.to_json();13471348return dict;1349}1350};13511352struct NativeSymbolInspectParams {1353String native_class;1354String symbol_name;13551356void load(const Dictionary &p_params) {1357native_class = p_params["native_class"];1358symbol_name = p_params["symbol_name"];1359}1360};13611362/**1363* Enum of known range kinds1364*/1365namespace FoldingRangeKind {1366/**1367* Folding range for a comment1368*/1369static const String Comment = "comment";1370/**1371* Folding range for a imports or includes1372*/1373static const String Imports = "imports";1374/**1375* Folding range for a region (e.g. `#region`)1376*/1377static const String Region = "region";1378} // namespace FoldingRangeKind13791380/**1381* Represents a folding range.1382*/1383struct FoldingRange {1384/**1385* The zero-based line number from where the folded range starts.1386*/1387int startLine = 0;13881389/**1390* The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line.1391*/1392int startCharacter = 0;13931394/**1395* The zero-based line number where the folded range ends.1396*/1397int endLine = 0;13981399/**1400* The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.1401*/1402int endCharacter = 0;14031404/**1405* Describes the kind of the folding range such as `comment' or 'region'. The kind1406* is used to categorize folding ranges and used by commands like 'Fold all comments'. See1407* [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds.1408*/1409String kind = FoldingRangeKind::Region;14101411_FORCE_INLINE_ Dictionary to_json() const {1412Dictionary dict;1413dict["startLine"] = startLine;1414dict["startCharacter"] = startCharacter;1415dict["endLine"] = endLine;1416dict["endCharacter"] = endCharacter;1417return dict;1418}1419};14201421// Use namespace instead of enumeration to follow the LSP specifications1422/**1423* How a completion was triggered1424*/1425namespace CompletionTriggerKind {1426/**1427* Completion was triggered by typing an identifier (24x7 code1428* complete), manual invocation (e.g Ctrl+Space) or via API.1429*/1430static const int Invoked = 1;14311432/**1433* Completion was triggered by a trigger character specified by1434* the `triggerCharacters` properties of the `CompletionRegistrationOptions`.1435*/1436static const int TriggerCharacter = 2;14371438/**1439* Completion was re-triggered as the current completion list is incomplete.1440*/1441static const int TriggerForIncompleteCompletions = 3;1442} // namespace CompletionTriggerKind14431444/**1445* Contains additional information about the context in which a completion request is triggered.1446*/1447struct CompletionContext {1448/**1449* How the completion was triggered.1450*/1451int triggerKind = CompletionTriggerKind::Invoked;14521453/**1454* The trigger character (a single character) that has trigger code complete.1455* Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`1456*/1457String triggerCharacter;14581459void load(const Dictionary &p_params) {1460triggerKind = int(p_params["triggerKind"]);1461triggerCharacter = p_params.get("triggerCharacter", "");1462}1463};14641465struct CompletionParams : public TextDocumentPositionParams {1466/**1467* The completion context. This is only available if the client specifies1468* to send this using `ClientCapabilities.textDocument.completion.contextSupport === true`1469*/1470CompletionContext context;14711472void load(const Dictionary &p_params) {1473TextDocumentPositionParams::load(p_params);14741475if (p_params.has("context")) {1476context.load(p_params["context"]);1477}1478}14791480Dictionary to_json() {1481Dictionary ctx;1482ctx["triggerCharacter"] = context.triggerCharacter;1483ctx["triggerKind"] = context.triggerKind;14841485Dictionary dict;1486dict = TextDocumentPositionParams::to_json();1487dict["context"] = ctx;1488return dict;1489}1490};14911492/**1493* The result of a hover request.1494*/1495struct Hover {1496/**1497* The hover's content1498*/1499MarkupContent contents;15001501/**1502* An optional range is a range inside a text document1503* that is used to visualize a hover, e.g. by changing the background color.1504*/1505Range range;15061507_FORCE_INLINE_ Dictionary to_json() const {1508Dictionary dict;1509dict["range"] = range.to_json();1510dict["contents"] = contents.to_json();1511return dict;1512}1513};15141515/**1516* Represents a parameter of a callable-signature. A parameter can1517* have a label and a doc-comment.1518*/1519struct ParameterInformation {1520/**1521* The label of this parameter information.1522*1523* Either a string or an inclusive start and exclusive end offsets within its containing1524* signature label. (see SignatureInformation.label). The offsets are based on a UTF-161525* string representation as `Position` and `Range` does.1526*1527* *Note*: a label of type string should be a substring of its containing signature label.1528* Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.1529*/1530String label;15311532/**1533* The human-readable doc-comment of this parameter. Will be shown1534* in the UI but can be omitted.1535*/1536MarkupContent documentation;15371538Dictionary to_json() const {1539Dictionary dict;1540dict["label"] = label;1541dict["documentation"] = documentation.to_json();1542return dict;1543}1544};15451546/**1547* Represents the signature of something callable. A signature1548* can have a label, like a function-name, a doc-comment, and1549* a set of parameters.1550*/1551struct SignatureInformation {1552/**1553* The label of this signature. Will be shown in1554* the UI.1555*/1556String label;15571558/**1559* The human-readable doc-comment of this signature. Will be shown1560* in the UI but can be omitted.1561*/1562MarkupContent documentation;15631564/**1565* The parameters of this signature.1566*/1567Vector<ParameterInformation> parameters;15681569Dictionary to_json() const {1570Dictionary dict;1571dict["label"] = label;1572dict["documentation"] = documentation.to_json();1573Array args;1574for (int i = 0; i < parameters.size(); i++) {1575args.push_back(parameters[i].to_json());1576}1577dict["parameters"] = args;1578return dict;1579}1580};15811582/**1583* Signature help represents the signature of something1584* callable. There can be multiple signature but only one1585* active and only one active parameter.1586*/1587struct SignatureHelp {1588/**1589* One or more signatures.1590*/1591Vector<SignatureInformation> signatures;15921593/**1594* The active signature. If omitted or the value lies outside the1595* range of `signatures` the value defaults to zero or is ignored if1596* `signatures.length === 0`. Whenever possible implementers should1597* make an active decision about the active signature and shouldn't1598* rely on a default value.1599* In future version of the protocol this property might become1600* mandatory to better express this.1601*/1602int activeSignature = 0;16031604/**1605* The active parameter of the active signature. If omitted or the value1606* lies outside the range of `signatures[activeSignature].parameters`1607* defaults to 0 if the active signature has parameters. If1608* the active signature has no parameters it is ignored.1609* In future version of the protocol this property might become1610* mandatory to better express the active parameter if the1611* active signature does have any.1612*/1613int activeParameter = 0;16141615Dictionary to_json() const {1616Dictionary dict;1617Array sigs;1618for (int i = 0; i < signatures.size(); i++) {1619sigs.push_back(signatures[i].to_json());1620}1621dict["signatures"] = sigs;1622dict["activeSignature"] = activeSignature;1623dict["activeParameter"] = activeParameter;1624return dict;1625}1626};16271628/**1629* A pattern to describe in which file operation requests or notifications1630* the server is interested in.1631*/1632struct FileOperationPattern {1633/**1634* The glob pattern to match.1635*/1636String glob = "**/*.gd";16371638/**1639* Whether to match `file`s or `folder`s with this pattern.1640*1641* Matches both if undefined.1642*/1643String matches = "file";16441645Dictionary to_json() const {1646Dictionary dict;16471648dict["glob"] = glob;1649dict["matches"] = matches;16501651return dict;1652}1653};16541655/**1656* A filter to describe in which file operation requests or notifications1657* the server is interested in.1658*/1659struct FileOperationFilter {1660/**1661* The actual file operation pattern.1662*/1663FileOperationPattern pattern;16641665Dictionary to_json() const {1666Dictionary dict;16671668dict["pattern"] = pattern.to_json();16691670return dict;1671}1672};16731674/**1675* The options to register for file operations.1676*/1677struct FileOperationRegistrationOptions {1678/**1679* The actual filters.1680*/1681Vector<FileOperationFilter> filters;16821683FileOperationRegistrationOptions() {1684filters.push_back(FileOperationFilter());1685}16861687Dictionary to_json() const {1688Dictionary dict;16891690Array filts;1691for (int i = 0; i < filters.size(); i++) {1692filts.push_back(filters[i].to_json());1693}1694dict["filters"] = filts;16951696return dict;1697}1698};16991700/**1701* The server is interested in file notifications/requests.1702*/1703struct FileOperations {1704/**1705* The server is interested in receiving didDeleteFiles file notifications.1706*/1707FileOperationRegistrationOptions didDelete;17081709Dictionary to_json() const {1710Dictionary dict;17111712dict["didDelete"] = didDelete.to_json();17131714return dict;1715}1716};17171718/**1719* Workspace specific server capabilities1720*/1721struct Workspace {1722Dictionary to_json() const {1723Dictionary dict;1724return dict;1725}1726};17271728struct ServerCapabilities {1729/**1730* Defines how text documents are synced. Is either a detailed structure defining each notification or1731* for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`.1732*/1733TextDocumentSyncOptions textDocumentSync;17341735/**1736* The server provides hover support.1737*/1738bool hoverProvider = true;17391740/**1741* The server provides completion support.1742*/1743CompletionOptions completionProvider;17441745/**1746* The server provides signature help support.1747*/1748SignatureHelpOptions signatureHelpProvider;17491750/**1751* The server provides goto definition support.1752*/1753bool definitionProvider = true;17541755/**1756* The server provides Goto Type Definition support.1757*1758* Since 3.6.01759*/1760bool typeDefinitionProvider = false;17611762/**1763* The server provides Goto Implementation support.1764*1765* Since 3.6.01766*/1767bool implementationProvider = false;17681769/**1770* The server provides find references support.1771*/1772bool referencesProvider = true;17731774/**1775* The server provides document highlight support.1776*/1777bool documentHighlightProvider = true;17781779/**1780* The server provides document symbol support.1781*/1782bool documentSymbolProvider = true;17831784/**1785* The server provides workspace symbol support.1786*/1787bool workspaceSymbolProvider = false;17881789/**1790* The server supports workspace folder.1791*/1792Workspace workspace;17931794/**1795* The server provides code actions. The `CodeActionOptions` return type is only1796* valid if the client signals code action literal support via the property1797* `textDocument.codeAction.codeActionLiteralSupport`.1798*/1799bool codeActionProvider = false;18001801/**1802* The server provides code lens.1803*/1804CodeLensOptions codeLensProvider;18051806/**1807* The server provides document formatting.1808*/1809bool documentFormattingProvider = false;18101811/**1812* The server provides document range formatting.1813*/1814bool documentRangeFormattingProvider = false;18151816/**1817* The server provides document formatting on typing.1818*/1819DocumentOnTypeFormattingOptions documentOnTypeFormattingProvider;18201821/**1822* The server provides rename support. RenameOptions may only be1823* specified if the client states that it supports1824* `prepareSupport` in its initial `initialize` request.1825*/1826RenameOptions renameProvider;18271828/**1829* The server provides document link support.1830*/1831DocumentLinkOptions documentLinkProvider;18321833/**1834* The server provides color provider support.1835*1836* Since 3.6.01837*/1838ColorProviderOptions colorProvider;18391840/**1841* The server provides folding provider support.1842*1843* Since 3.10.01844*/1845FoldingRangeProviderOptions foldingRangeProvider;18461847/**1848* The server provides go to declaration support.1849*1850* Since 3.14.01851*/1852bool declarationProvider = true;18531854/**1855* The server provides execute command support.1856*/1857ExecuteCommandOptions executeCommandProvider;18581859_FORCE_INLINE_ Dictionary to_json() {1860Dictionary dict;1861dict["textDocumentSync"] = textDocumentSync.to_json();1862dict["completionProvider"] = completionProvider.to_json();1863signatureHelpProvider.triggerCharacters.push_back(",");1864signatureHelpProvider.triggerCharacters.push_back("(");1865dict["signatureHelpProvider"] = signatureHelpProvider.to_json();1866//dict["codeLensProvider"] = codeLensProvider.to_json();1867dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();1868dict["renameProvider"] = renameProvider.to_json();1869dict["documentLinkProvider"] = documentLinkProvider.to_json();1870dict["colorProvider"] = false; // colorProvider.to_json();1871dict["foldingRangeProvider"] = false; //foldingRangeProvider.to_json();1872dict["executeCommandProvider"] = executeCommandProvider.to_json();1873dict["hoverProvider"] = hoverProvider;1874dict["definitionProvider"] = definitionProvider;1875dict["typeDefinitionProvider"] = typeDefinitionProvider;1876dict["implementationProvider"] = implementationProvider;1877dict["referencesProvider"] = referencesProvider;1878dict["documentHighlightProvider"] = documentHighlightProvider;1879dict["documentSymbolProvider"] = documentSymbolProvider;1880dict["workspaceSymbolProvider"] = workspaceSymbolProvider;1881dict["workspace"] = workspace.to_json();1882dict["codeActionProvider"] = codeActionProvider;1883dict["documentFormattingProvider"] = documentFormattingProvider;1884dict["documentRangeFormattingProvider"] = documentRangeFormattingProvider;1885dict["declarationProvider"] = declarationProvider;1886return dict;1887}1888};18891890struct InitializeResult {1891/**1892* The capabilities the language server provides.1893*/1894ServerCapabilities capabilities;18951896_FORCE_INLINE_ Dictionary to_json() {1897Dictionary dict;1898dict["capabilities"] = capabilities.to_json();1899return dict;1900}1901};19021903struct GodotNativeClassInfo {1904String name;1905const DocData::ClassDoc *class_doc = nullptr;1906const ClassDB::ClassInfo *class_info = nullptr;19071908Dictionary to_json() const {1909Dictionary dict;1910dict["name"] = name;1911dict["inherits"] = class_doc->inherits;1912return dict;1913}1914};19151916/** Features not included in the standard lsp specifications */1917struct GodotCapabilities {1918/**1919* Native class list1920*/1921List<GodotNativeClassInfo> native_classes;19221923Dictionary to_json() const {1924Dictionary dict;1925Array classes;1926for (const GodotNativeClassInfo &native_class : native_classes) {1927classes.push_back(native_class.to_json());1928}1929dict["native_classes"] = classes;1930return dict;1931}1932};19331934/** Format BBCode documentation from DocData to markdown */1935static String marked_documentation(const String &p_bbcode) {1936String markdown = p_bbcode.strip_edges();19371938Vector<String> lines = markdown.split("\n");1939bool in_codeblock_tag = false;1940// This is for handling the special [codeblocks] syntax used by the built-in class reference.1941bool in_codeblocks_tag = false;1942bool in_codeblocks_gdscript_tag = false;19431944markdown = "";1945for (int i = 0; i < lines.size(); i++) {1946String line = lines[i];19471948// For [codeblocks] tags we locate a child [gdscript] tag and turn that1949// into a GDScript code listing. Other languages and the surrounding tag1950// are skipped.1951if (line.contains("[codeblocks]")) {1952in_codeblocks_tag = true;1953continue;1954}1955if (in_codeblocks_tag && line.contains("[/codeblocks]")) {1956in_codeblocks_tag = false;1957continue;1958}1959if (in_codeblocks_tag) {1960if (line.contains("[gdscript]")) {1961in_codeblocks_gdscript_tag = true;1962line = "```gdscript";1963} else if (in_codeblocks_gdscript_tag && line.contains("[/gdscript]")) {1964line = "```";1965in_codeblocks_gdscript_tag = false;1966} else if (!in_codeblocks_gdscript_tag) {1967continue;1968}1969}19701971// We need to account for both [codeblock] and [codeblock lang=...].1972String codeblock_lang = "gdscript";1973int block_start = line.find("[codeblock");1974if (block_start != -1) {1975int bracket_pos = line.find_char(']', block_start);1976if (bracket_pos != -1) {1977int lang_start = line.find("lang=", block_start);1978if (lang_start != -1 && lang_start < bracket_pos) {1979constexpr int LANG_PARAM_LENGTH = 5; // Length of "lang=".1980int lang_value_start = lang_start + LANG_PARAM_LENGTH;1981int lang_end = bracket_pos;1982if (lang_value_start < lang_end) {1983codeblock_lang = line.substr(lang_value_start, lang_end - lang_value_start);1984}1985}1986in_codeblock_tag = true;1987line = "```" + codeblock_lang;1988}1989}19901991if (in_codeblock_tag && line.contains("[/codeblock]")) {1992line = "```";1993in_codeblock_tag = false;1994}19951996if (!in_codeblock_tag) {1997line = line.strip_edges();1998line = line.replace("[br]", "\n\n");19992000line = line.replace("[code]", "`");2001line = line.replace("[/code]", "`");2002line = line.replace("[i]", "*");2003line = line.replace("[/i]", "*");2004line = line.replace("[b]", "**");2005line = line.replace("[/b]", "**");2006line = line.replace("[u]", "__");2007line = line.replace("[/u]", "__");2008line = line.replace("[s]", "~~");2009line = line.replace("[/s]", "~~");2010line = line.replace("[kbd]", "`");2011line = line.replace("[/kbd]", "`");2012line = line.replace("[center]", "");2013line = line.replace("[/center]", "");2014line = line.replace("[/font]", "");2015line = line.replace("[/color]", "");2016line = line.replace("[/img]", "");20172018// Convert remaining simple bracketed class names to backticks and literal brackets.2019// This handles cases like [Node2D], [Sprite2D], etc. and [lb] and [rb].2020int pos = 0;2021while ((pos = line.find_char('[', pos)) != -1) {2022// Replace the special cases for [lb] and [rb] first and walk2023// past them to avoid conflicts with class names.2024const bool is_within_bounds = pos + 4 <= line.length();2025if (is_within_bounds && line.substr(pos, 4) == "[lb]") {2026line = line.substr(0, pos) + "\\[" + line.substr(pos + 4);2027// We advance past the newly inserted `\\` and `[` characters (2 chars) so the2028// next `line.find()` does not stop at the same position.2029pos += 2;2030continue;2031} else if (is_within_bounds && line.substr(pos, 4) == "[rb]") {2032line = line.substr(0, pos) + "\\]" + line.substr(pos + 4);2033pos += 2;2034continue;2035}20362037// Replace class names in brackets.2038int end_pos = line.find_char(']', pos);2039if (end_pos == -1) {2040break;2041}20422043String content = line.substr(pos + 1, end_pos - pos - 1);2044// We only convert if it looks like a simple class name (no spaces, no special chars).2045// GDScript supports unicode characters as identifiers so we only exclude markers of other BBCode tags to avoid conflicts.2046bool is_class_name = (!content.is_empty() && content != "url" && !content.contains_char(' ') && !content.contains_char('=') && !content.contains_char('/'));2047if (is_class_name) {2048line = line.substr(0, pos) + "`" + content + "`" + line.substr(end_pos + 1);2049pos += content.length() + 2;2050} else {2051pos = end_pos + 1;2052}2053}20542055constexpr int URL_OPEN_TAG_LENGTH = 5; // Length of "[url=".2056constexpr int URL_CLOSE_TAG_LENGTH = 6; // Length of "[/url]".20572058// This is for the case [url=$url]$text[/url].2059pos = 0;2060while ((pos = line.find("[url=", pos)) != -1) {2061int url_end = line.find_char(']', pos);2062int close_start = line.find("[/url]", url_end);2063if (url_end == -1 || close_start == -1) {2064break;2065}20662067String url = line.substr(pos + URL_OPEN_TAG_LENGTH, url_end - pos - URL_OPEN_TAG_LENGTH);2068String text = line.substr(url_end + 1, close_start - url_end - 1);2069String replacement = "[" + text + "](" + url + ")";2070line = line.substr(0, pos) + replacement + line.substr(close_start + URL_CLOSE_TAG_LENGTH);2071pos += replacement.length();2072}20732074// This is for the case [url]$url[/url].2075pos = 0;2076while ((pos = line.find("[url]", pos)) != -1) {2077int close_pos = line.find("[/url]", pos);2078if (close_pos == -1) {2079break;2080}20812082String url = line.substr(pos + URL_OPEN_TAG_LENGTH, close_pos - pos - URL_OPEN_TAG_LENGTH);2083String replacement = "[" + url + "](" + url + ")";2084line = line.substr(0, pos) + replacement + line.substr(close_pos + URL_CLOSE_TAG_LENGTH);2085pos += replacement.length();2086}20872088// Replace the various link types with inline code ([class MyNode] to `MyNode`).2089// Uses a while loop because there can occasionally be multiple links of the same type in a single line.2090const Vector<String> link_start_patterns = {2091"[class ", "[method ", "[member ", "[signal ", "[enum ", "[constant ",2092"[annotation ", "[constructor ", "[operator ", "[theme_item ", "[param "2093};2094for (const String &pattern : link_start_patterns) {2095int pattern_pos = 0;2096while ((pattern_pos = line.find(pattern, pattern_pos)) != -1) {2097int end_pos = line.find_char(']', pattern_pos);2098if (end_pos == -1) {2099break;2100}21012102String content = line.substr(pattern_pos + pattern.length(), end_pos - pattern_pos - pattern.length());2103String replacement = "`" + content + "`";2104line = line.substr(0, pattern_pos) + replacement + line.substr(end_pos + 1);2105pattern_pos += replacement.length();2106}2107}21082109// Remove tags with attributes like [color=red], as they don't have a direct Markdown2110// equivalent supported by external tools.2111const String attribute_tags[] = {2112"color", "font", "img"2113};2114for (const String &tag_name : attribute_tags) {2115int tag_pos = 0;2116while ((tag_pos = line.find("[" + tag_name + "=", tag_pos)) != -1) {2117int end_pos = line.find_char(']', tag_pos);2118if (end_pos == -1) {2119break;2120}21212122line = line.substr(0, tag_pos) + line.substr(end_pos + 1);2123}2124}2125}21262127if (i < lines.size() - 1) {2128line += "\n";2129}2130markdown += line;2131}2132return markdown;2133}21342135/**2136* A document highlight is a range inside a text document which deserves2137* special attention. Usually a document highlight is visualized by changing2138* the background color of its range.2139*/2140struct DocumentHighlight {2141/**2142* The range this highlight applies to.2143*/2144Range range;21452146_FORCE_INLINE_ Dictionary to_json() const {2147Dictionary dict;2148dict["range"] = range.to_json();2149return dict;2150}21512152_FORCE_INLINE_ void load(const Dictionary &p_params) {2153range.load(p_params["range"]);2154}2155};2156} // namespace LSP215721582159