diff options
Diffstat (limited to 'modules/gdscript')
18 files changed, 498 insertions, 255 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 4efa90fd86..1d0567dd8d 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -590,10 +590,10 @@ extends Sprite var elapsed = 0.0 func _process(delta): - var min_angle = deg2rad(0.0) - var max_angle = deg2rad(90.0) - rotation = lerp_angle(min_angle, max_angle, elapsed) - elapsed += delta + var min_angle = deg2rad(0.0) + var max_angle = deg2rad(90.0) + rotation = lerp_angle(min_angle, max_angle, elapsed) + elapsed += delta [/codeblock] </description> </method> @@ -694,6 +694,14 @@ [/codeblock] </description> </method> + <method name="ord"> + <return type="int"> + </return> + <argument index="0" name="char" type="String"> + </argument> + <description> + </description> + </method> <method name="parse_json"> <return type="Variant"> </return> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 963b40529d..4d6279074c 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -247,7 +247,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ in_function_args = false; } - if (expect_type && prev_is_char) { + if (expect_type && (prev_is_char || str[j] == '=')) { expect_type = false; } @@ -364,20 +364,28 @@ void GDScriptSyntaxHighlighter::_update_cache() { number_color = text_editor->get_color("number_color"); member_color = text_editor->get_color("member_variable_color"); - EditorSettings *settings = EditorSettings::get_singleton(); - String text_editor_color_theme = settings->get("text_editor/theme/color_theme"); + const String text_editor_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme"); + const bool default_theme = text_editor_color_theme == "Default"; - bool default_theme = text_editor_color_theme == "Default"; - bool dark_theme = settings->is_dark_theme(); - - function_definition_color = default_theme ? Color(0.0, 0.88, 1.0) : dark_theme ? Color(0.0, 0.88, 1.0) : Color(0.0, 0.65, 0.73); - node_path_color = default_theme ? Color(0.39, 0.76, 0.35) : dark_theme ? Color(0.39, 0.76, 0.35) : Color(0.32, 0.55, 0.29); + if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) { + function_definition_color = Color(0.4, 0.9, 1.0); + node_path_color = Color(0.39, 0.76, 0.35); + } else { + function_definition_color = Color(0.0, 0.65, 0.73); + node_path_color = Color(0.32, 0.55, 0.29); + } EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color); EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color); if (text_editor_color_theme == "Adaptive" || default_theme) { - settings->set_initial_value("text_editor/highlighting/gdscript/function_definition_color", function_definition_color, true); - settings->set_initial_value("text_editor/highlighting/gdscript/node_path_color", node_path_color, true); + EditorSettings::get_singleton()->set_initial_value( + "text_editor/highlighting/gdscript/function_definition_color", + function_definition_color, + true); + EditorSettings::get_singleton()->set_initial_value( + "text_editor/highlighting/gdscript/node_path_color", + node_path_color, + true); } function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color"); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 5dab063061..db7f8d22e6 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -99,7 +99,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco #endif instance->owner->set_script_instance(instance); - /* STEP 2, INITIALIZE AND CONSRTUCT */ + /* STEP 2, INITIALIZE AND CONSTRUCT */ #ifndef NO_THREADS GDScriptLanguage::singleton->lock->lock(); @@ -2177,11 +2177,11 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); Error err = script->load_byte_code(p_path); - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load byte code from file '" + p_path + "'."); } else { Error err = script->load_source_code(p_path); - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load source code from file '" + p_path + "'."); script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); @@ -2217,7 +2217,7 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND(!file); + ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'."); String source = file->get_as_utf8_string(); if (source.empty()) { @@ -2244,10 +2244,7 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resou Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) { - - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7166189416..dea2225e91 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1520,8 +1520,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo if (ret2 < 0) return ERR_PARSE_ERROR; + int message_ret = 0; + if (as->message) { + message_ret = _parse_expression(codegen, as->message, p_stack_level + 1, false); + if (message_ret < 0) + return ERR_PARSE_ERROR; + } + codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSERT); codegen.opcodes.push_back(ret2); + codegen.opcodes.push_back(message_ret); #endif } break; case GDScriptParser::Node::TYPE_BREAKPOINT: { @@ -2064,7 +2072,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa } instance->owner->set_script_instance(instance); - /* STEP 2, INITIALIZE AND CONSRTUCT */ + /* STEP 2, INITIALIZE AND CONSTRUCT */ Variant::CallError ce; p_script->initializer->call(instance, NULL, 0, ce); diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 68f2a9473e..83d02e4977 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1419,7 +1419,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (!container->iter_init(*counter, valid)) { #ifdef DEBUG_ENABLED if (!valid) { - err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "'."; + err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'."; OPCODE_BREAK; } #endif @@ -1432,7 +1432,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a *iterator = container->iter_get(*counter, valid); #ifdef DEBUG_ENABLED if (!valid) { - err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "'."; + err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'."; OPCODE_BREAK; } #endif @@ -1452,7 +1452,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (!container->iter_next(*counter, valid)) { #ifdef DEBUG_ENABLED if (!valid) { - err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?)."; + err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?)."; OPCODE_BREAK; } #endif @@ -1465,7 +1465,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a *iterator = container->iter_get(*counter, valid); #ifdef DEBUG_ENABLED if (!valid) { - err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?)."; + err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?)."; OPCODE_BREAK; } #endif @@ -1475,20 +1475,25 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSERT) { - CHECK_SPACE(2); + CHECK_SPACE(3); #ifdef DEBUG_ENABLED GET_VARIANT_PTR(test, 1); + GET_VARIANT_PTR(message, 2); bool result = test->booleanize(); if (!result) { - - err_text = "Assertion failed."; + const String &message_str = *message; + if (message_str.empty()) { + err_text = "Assertion failed."; + } else { + err_text = "Assertion failed: " + message_str; + } OPCODE_BREAK; } #endif - ip += 2; + ip += 3; } DISPATCH_OPCODE; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 97790e00bb..d9535d0f1f 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -572,37 +572,31 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ } break; case OBJ_WEAKREF: { VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::OBJECT) { - + if (p_args[0]->get_type() == Variant::OBJECT) { + if (p_args[0]->is_ref()) { + Ref<WeakRef> wref = memnew(WeakRef); + REF r = *p_args[0]; + if (r.is_valid()) { + wref->set_ref(r); + } + r_ret = wref; + } else { + Ref<WeakRef> wref = memnew(WeakRef); + Object *obj = *p_args[0]; + if (obj) { + wref->set_obj(obj); + } + r_ret = wref; + } + } else if (p_args[0]->get_type() == Variant::NIL) { + r_ret = memnew(WeakRef); + } else { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::OBJECT; r_ret = Variant(); return; } - - if (p_args[0]->is_ref()) { - - REF r = *p_args[0]; - if (!r.is_valid()) { - r_ret = Variant(); - return; - } - - Ref<WeakRef> wref = memnew(WeakRef); - wref->set_ref(r); - r_ret = wref; - } else { - Object *obj = *p_args[0]; - if (!obj) { - r_ret = Variant(); - return; - } - Ref<WeakRef> wref = memnew(WeakRef); - wref->set_obj(obj); - r_ret = wref; - } - } break; case FUNC_FUNCREF: { VALIDATE_ARG_COUNT(2); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index e96bf0238a..21434cd150 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1207,7 +1207,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (_get_completable_identifier(COMPLETION_INDEX, identifier)) { if (identifier == StringName()) { - identifier = "@temp"; //so it parses allright + identifier = "@temp"; //so it parses alright } completion_node = op; @@ -1399,9 +1399,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s unary = true; break; case OperatorNode::OP_NEG: - priority = 1; - unary = true; - break; case OperatorNode::OP_POS: priority = 1; unary = true; @@ -2846,15 +2843,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { assigned = subexpr; } else { - ConstantNode *c = alloc_node<ConstantNode>(); - if (lv->datatype.has_type && lv->datatype.kind == DataType::BUILTIN) { - Variant::CallError err; - c->value = Variant::construct(lv->datatype.builtin_type, NULL, 0, err); - } else { - c->value = Variant(); - } - c->line = var_line; - assigned = c; + assigned = _get_default_value_for_type(lv->datatype, var_line); } lv->assign = assigned; //must be added later, to avoid self-referencing. @@ -3280,15 +3269,36 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { case GDScriptTokenizer::TK_PR_ASSERT: { tokenizer->advance(); - Node *condition = _parse_and_reduce_expression(p_block, p_static); - if (!condition) { - if (_recover_from_completion()) { - break; - } + + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after assert"); + return; + } + + tokenizer->advance(); + + Vector<Node *> args; + const bool result = _parse_arguments(p_block, args, p_static); + if (!result) { + return; + } + + if (args.empty() || args.size() > 2) { + _set_error("Wrong number of arguments, expected 1 or 2"); return; } + AssertNode *an = alloc_node<AssertNode>(); - an->condition = condition; + an->condition = _reduce_expression(args[0], p_static); + + if (args.size() == 2) { + an->message = _reduce_expression(args[1], p_static); + } else { + ConstantNode *message_node = alloc_node<ConstantNode>(); + message_node->value = String(); + an->message = message_node; + } + p_block->statements.push_back(an); if (!_end_statement()) { @@ -3533,6 +3543,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } + if (p_class->classname_used && ProjectSettings::get_singleton()->has_setting("autoload/" + p_class->name)) { + const String autoload_path = ProjectSettings::get_singleton()->get_setting("autoload/" + p_class->name); + if (autoload_path.begins_with("*")) { + // It's a singleton, and not just a regular AutoLoad script. + _set_error("The class \"" + p_class->name + "\" conflicts with the AutoLoad singleton of the same name, and is therefore redundant. Remove the class_name declaration to fix this error."); + } + return; + } + tokenizer->advance(2); if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { @@ -4839,35 +4858,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - Variant::Type initial_type = member.data_type.has_type ? member.data_type.builtin_type : member._export.type; + Node *expr; - if (initial_type != Variant::NIL && initial_type != Variant::OBJECT) { - IdentifierNode *id = alloc_node<IdentifierNode>(); - id->name = member.identifier; - - Node *expr; + if (member.data_type.has_type) { + expr = _get_default_value_for_type(member.data_type); + } else { + DataType exported_type; + exported_type.has_type = true; + exported_type.kind = DataType::BUILTIN; + exported_type.builtin_type = member._export.type; + expr = _get_default_value_for_type(exported_type); + } - // Make sure arrays and dictionaries are not shared - if (initial_type == Variant::ARRAY) { - expr = alloc_node<ArrayNode>(); - } else if (initial_type == Variant::DICTIONARY) { - expr = alloc_node<DictionaryNode>(); - } else { - ConstantNode *cn = alloc_node<ConstantNode>(); - Variant::CallError ce2; - cn->value = Variant::construct(initial_type, NULL, 0, ce2); - expr = cn; - } + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name = member.identifier; - OperatorNode *op = alloc_node<OperatorNode>(); - op->op = OperatorNode::OP_INIT_ASSIGN; - op->arguments.push_back(id); - op->arguments.push_back(expr); + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OperatorNode::OP_INIT_ASSIGN; + op->arguments.push_back(id); + op->arguments.push_back(expr); - p_class->initializer->statements.push_back(op); + p_class->initializer->statements.push_back(op); - member.initial_assignment = op; - } + member.initial_assignment = op; } if (autoexport && member.data_type.has_type) { @@ -5727,28 +5740,35 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, } } - // Still look for class constants in parent script + // Still look for class constants in parent scripts if (!found && (base_type.kind == DataType::GDSCRIPT || base_type.kind == DataType::SCRIPT)) { Ref<Script> scr = base_type.script_type; ERR_FAIL_COND_V(scr.is_null(), result); - Map<StringName, Variant> constants; - scr->get_constants(&constants); + while (scr.is_valid()) { + Map<StringName, Variant> constants; + scr->get_constants(&constants); - if (constants.has(id)) { - Ref<GDScript> gds = constants[id]; + if (constants.has(id)) { + Ref<GDScript> gds = constants[id]; - if (gds.is_valid()) { - result.kind = DataType::GDSCRIPT; - result.script_type = gds; - found = true; - } else { - Ref<Script> scr2 = constants[id]; - if (scr2.is_valid()) { - result.kind = DataType::SCRIPT; - result.script_type = scr2; + if (gds.is_valid()) { + result.kind = DataType::GDSCRIPT; + result.script_type = gds; found = true; + } else { + Ref<Script> scr2 = constants[id]; + if (scr2.is_valid()) { + result.kind = DataType::SCRIPT; + result.script_type = scr2; + found = true; + } } } + if (found) { + break; + } else { + scr = scr->get_base_script(); + } } } @@ -6140,6 +6160,31 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data return false; } +GDScriptParser::Node *GDScriptParser::_get_default_value_for_type(const DataType &p_type, int p_line) { + Node *result; + + if (p_type.has_type && p_type.kind == DataType::BUILTIN && p_type.builtin_type != Variant::NIL && p_type.builtin_type != Variant::OBJECT) { + if (p_type.builtin_type == Variant::ARRAY) { + result = alloc_node<ArrayNode>(); + } else if (p_type.builtin_type == Variant::DICTIONARY) { + result = alloc_node<DictionaryNode>(); + } else { + ConstantNode *c = alloc_node<ConstantNode>(); + Variant::CallError err; + c->value = Variant::construct(p_type.builtin_type, NULL, 0, err); + result = c; + } + } else { + ConstantNode *c = alloc_node<ConstantNode>(); + c->value = Variant(); + result = c; + } + + result->line = p_line; + + return result; +} + GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { #ifdef DEBUG_ENABLED if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) { @@ -6655,7 +6700,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } - p_node->set_datatype(_resolve_type(node_type, p_node->line)); + node_type = _resolve_type(node_type, p_node->line); + p_node->set_datatype(node_type); return node_type; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 72aa819a8c..04ce9cf4c6 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -481,7 +481,12 @@ public: struct AssertNode : public Node { Node *condition; - AssertNode() { type = TYPE_ASSERT; } + Node *message; + AssertNode() : + condition(0), + message(0) { + type = TYPE_ASSERT; + } }; struct BreakpointNode : public Node { @@ -610,6 +615,7 @@ private: bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const; bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const; bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const; + Node *_get_default_value_for_type(const DataType &p_type, int p_line = -1); DataType _reduce_node_type(Node *p_node); DataType _reduce_function_call_type(const OperatorNode *p_call); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 64b354bdb8..8b20b0ff48 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1425,7 +1425,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) int len; // Objects cannot be constant, never encode objects Error err = encode_variant(E->get(), NULL, len, false); - ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); + ERR_FAIL_COND_V_MSG(err != OK, Vector<uint8_t>(), "Error when trying to encode Variant."); int pos = buf.size(); buf.resize(pos + len); encode_variant(E->get(), &buf.write[pos], len, false); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 45f9ec9c6a..03d731a5f0 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -105,6 +105,40 @@ void ExtendGDScriptParser::update_symbols() { } } +void ExtendGDScriptParser::update_document_links(const String &p_code) { + document_links.clear(); + + GDScriptTokenizerText tokenizer; + FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); + tokenizer.set_code(p_code); + while (true) { + if (tokenizer.get_token() == GDScriptTokenizer::TK_EOF) { + break; + } else if (tokenizer.get_token() == GDScriptTokenizer::TK_CONSTANT) { + Variant const_val = tokenizer.get_token_constant(); + if (const_val.get_type() == Variant::STRING) { + String path = const_val; + bool exists = fs->file_exists(path); + if (!exists) { + path = get_path().get_base_dir() + "/" + path; + exists = fs->file_exists(path); + } + if (exists) { + String value = const_val; + lsp::DocumentLink link; + link.target = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path); + link.range.start.line = LINE_NUMBER_TO_INDEX(tokenizer.get_token_line()); + link.range.end.line = link.range.start.line; + link.range.end.character = LINE_NUMBER_TO_INDEX(tokenizer.get_token_column()); + link.range.start.character = link.range.end.character - value.length(); + document_links.push_back(link); + } + } + } + tokenizer.advance(); + } +} + void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol) { const String uri = get_uri(); @@ -123,7 +157,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p r_symbol.selectionRange.start.line = r_symbol.range.start.line; r_symbol.detail = "class " + r_symbol.name; bool is_root_class = &r_symbol == &class_symbol; - r_symbol.documentation = parse_documentation_as_markdown(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class); + r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class); for (int i = 0; i < p_class->variables.size(); ++i) { @@ -150,7 +184,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.detail += " = " + JSON::print(m.default_value); } - symbol.documentation = parse_documentation_as_markdown(line); + symbol.documentation = parse_documentation(line); symbol.uri = uri; symbol.script_path = path; @@ -170,7 +204,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.range.end.line = symbol.range.start.line; symbol.range.end.character = lines[line].length(); symbol.selectionRange.start.line = symbol.range.start.line; - symbol.documentation = parse_documentation_as_markdown(line); + symbol.documentation = parse_documentation(line); symbol.uri = uri; symbol.script_path = path; symbol.detail = "signal " + signal.name + "("; @@ -189,6 +223,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p lsp::DocumentSymbol symbol; const GDScriptParser::ClassNode::Constant &c = E->value(); const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression); + ERR_FAIL_COND(!node); symbol.name = E->key(); symbol.kind = lsp::SymbolKind::Constant; symbol.deprecated = false; @@ -198,7 +233,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.range.end.line = symbol.range.start.line; symbol.range.end.character = lines[line].length(); symbol.selectionRange.start.line = symbol.range.start.line; - symbol.documentation = parse_documentation_as_markdown(line); + symbol.documentation = parse_documentation(line); symbol.uri = uri; symbol.script_path = path; @@ -266,7 +301,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN r_symbol.range.end.line = MAX(p_func->body->end_line - 2, p_func->body->line); r_symbol.range.end.character = lines[r_symbol.range.end.line].length(); r_symbol.selectionRange.start.line = r_symbol.range.start.line; - r_symbol.documentation = parse_documentation_as_markdown(line); + r_symbol.documentation = parse_documentation(line); r_symbol.uri = uri; r_symbol.script_path = path; @@ -324,67 +359,11 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) { symbol.detail += ": " + var->datatype.to_string(); } - symbol.documentation = parse_documentation_as_markdown(line); + symbol.documentation = parse_documentation(line); r_symbol.children.push_back(symbol); } } -String ExtendGDScriptParser::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; - - markdown = ""; - for (int i = 0; i < lines.size(); i++) { - String line = lines[i]; - int block_start = line.find("[codeblock]"); - if (block_start != -1) { - code_block_indent = block_start; - in_code_block = true; - line = "'''gdscript"; - line = "\n"; - } else if (in_code_block) { - line = "\t" + line.substr(code_block_indent, line.length()); - } - - if (in_code_block && line.find("[/codeblock]") != -1) { - line = "'''\n"; - line = "\n"; - in_code_block = false; - } - - if (!in_code_block) { - line = line.strip_edges(); - line = line.replace("[code]", "`"); - line = line.replace("[/code]", "`"); - line = line.replace("[i]", "*"); - line = line.replace("[/i]", "*"); - line = line.replace("[b]", "**"); - 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("[", "`"); - line = line.replace("]", "`"); - } - - if (!in_code_block && i < lines.size() - 1) { - line += "\n\n"; - } else if (i < lines.size() - 1) { - line += "\n"; - } - markdown += line; - } - return markdown; -} - String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { ERR_FAIL_INDEX_V(p_line, lines.size(), String()); @@ -543,10 +522,6 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i return ret; } -String ExtendGDScriptParser::parse_documentation_as_markdown(int p_line, bool p_docs_down) { - return marked_documentation(parse_documentation(p_line, p_docs_down)); -} - const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const { if (p_line <= 0) { return &class_symbol; @@ -573,6 +548,10 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String return NULL; } +const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const { + return document_links; +} + const Array &ExtendGDScriptParser::get_member_completions() { if (member_completions.empty()) { @@ -674,6 +653,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode const GDScriptParser::ClassNode::Constant &c = E->value(); const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression); + ERR_FAIL_COND_V(!node, class_api); Dictionary api; api["name"] = E->key(); @@ -755,5 +735,6 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) { Error err = GDScriptParser::parse(p_code, p_path.get_base_dir(), false, p_path, false, NULL, false); update_diagnostics(); update_symbols(); + update_document_links(p_code); return err; } diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index dd0453d3ff..a6e0ca5534 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -56,12 +56,14 @@ class ExtendGDScriptParser : public GDScriptParser { lsp::DocumentSymbol class_symbol; Vector<lsp::Diagnostic> diagnostics; + List<lsp::DocumentLink> document_links; ClassMembers members; HashMap<String, ClassMembers> inner_classes; void update_diagnostics(); void update_symbols(); + void update_document_links(const String &p_code); void parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol); void parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol); @@ -73,11 +75,6 @@ class ExtendGDScriptParser : public GDScriptParser { Array member_completions; - String parse_documentation_as_markdown(int p_line, bool p_docs_down = false); - -public: - static String marked_documentation(const String &p_bbcode); - public: _FORCE_INLINE_ const String &get_path() const { return path; } _FORCE_INLINE_ const Vector<String> &get_lines() const { return lines; } @@ -93,6 +90,7 @@ public: const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line) const; const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const; + const List<lsp::DocumentLink> &get_document_links() const; const Array &get_member_completions(); Dictionary generate_api() const; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index afe461b68e..ae2aaf6aee 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -100,9 +100,10 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { String root_uri = p_params["rootUri"]; String root = p_params["rootPath"]; - bool is_same_workspace = root == workspace->root; + bool is_same_workspace; +#ifndef WINDOWS_ENABLED is_same_workspace = root.to_lower() == workspace->root.to_lower(); -#ifdef WINDOWS_ENABLED +#else is_same_workspace = root.replace("\\", "/").to_lower() == workspace->root.to_lower(); #endif @@ -142,6 +143,7 @@ void GDScriptLanguageProtocol::poll() { Error GDScriptLanguageProtocol::start(int p_port) { if (server == NULL) { server = dynamic_cast<WebSocketServer *>(ClassDB::instance("WebSocketServer")); + ERR_FAIL_COND_V(!server, FAILED); server->set_buffers(8192, 1024, 8192, 1024); // 8mb should be way more than enough server->connect("data_received", this, "on_data_received"); server->connect("client_connected", this, "on_client_connected"); @@ -151,7 +153,13 @@ Error GDScriptLanguageProtocol::start(int p_port) { } void GDScriptLanguageProtocol::stop() { + const int *ptr = clients.next(NULL); + while (ptr) { + clients.get(*ptr)->close(); + ptr = clients.next(ptr); + } server->stop(); + clients.clear(); } void GDScriptLanguageProtocol::notify_all_clients(const String &p_method, const Variant &p_params) { diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 9bea4557ac..62c212a2bd 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -64,7 +64,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) { void GDScriptLanguageServer::start() { int port = (int)_EDITOR_GET("network/language_server/remote_port"); if (protocol.start(port) == OK) { - EditorNode::get_log()->add_message("** GDScript Language Server Started **"); + EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR); ERR_FAIL_COND(thread != NULL || thread_exit); thread_exit = false; thread = Thread::create(GDScriptLanguageServer::thread_main, this); @@ -78,7 +78,7 @@ void GDScriptLanguageServer::stop() { memdelete(thread); thread = NULL; protocol.stop(); - EditorNode::get_log()->add_message("** GDScript Language Server Stopped **"); + EditorNode::get_log()->add_message("--- GDScript language server stopped ---", EditorLog::MSG_TYPE_EDITOR); } void register_lsp_types() { diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index f211fae526..b83db718b8 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -39,6 +39,7 @@ void GDScriptTextDocument::_bind_methods() { ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen); ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange); + ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol); ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol); ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion); ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve); @@ -75,6 +76,11 @@ lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_ return doc; } +void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol *symbol) { + ERR_FAIL_NULL(symbol); + GDScriptLanguageProtocol::get_singleton()->notify_client("gdscript/show_native_symbol", symbol->to_json(true)); +} + void GDScriptTextDocument::initialize() { if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { @@ -102,6 +108,21 @@ void GDScriptTextDocument::initialize() { } } +Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) { + + Variant ret; + + lsp::NativeSymbolInspectParams params; + params.load(p_params); + + if (const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_native_symbol(params)) { + ret = symbol->to_json(true); + notify_client_show_symbol(symbol); + } + + return ret; +} + Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) { Dictionary params = p_params["textDocument"]; String uri = params["uri"]; @@ -271,8 +292,17 @@ Array GDScriptTextDocument::codeLens(const Dictionary &p_params) { return arr; } -Variant GDScriptTextDocument::documentLink(const Dictionary &p_params) { - Variant ret; +Array GDScriptTextDocument::documentLink(const Dictionary &p_params) { + Array ret; + + lsp::DocumentLinkParams params; + params.load(p_params); + + List<lsp::DocumentLink> links; + GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_document_links(params.textDocument.uri, links); + for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) { + ret.push_back(E->get().to_json()); + } return ret; } @@ -326,31 +356,35 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) { const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri); if (file_checker->file_exists(path)) { arr.push_back(location.to_json()); - } else if (!symbol->native_class.empty() && GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) { - String id; - switch (symbol->kind) { - case lsp::SymbolKind::Class: - id = "class_name:" + symbol->name; - break; - case lsp::SymbolKind::Constant: - id = "class_constant:" + symbol->native_class + ":" + symbol->name; - break; - case lsp::SymbolKind::Property: - case lsp::SymbolKind::Variable: - id = "class_property:" + symbol->native_class + ":" + symbol->name; - break; - case lsp::SymbolKind::Enum: - id = "class_enum:" + symbol->native_class + ":" + symbol->name; - break; - case lsp::SymbolKind::Method: - case lsp::SymbolKind::Function: - id = "class_method:" + symbol->native_class + ":" + symbol->name; - break; - default: - id = "class_global:" + symbol->native_class + ":" + symbol->name; - break; + } else if (!symbol->native_class.empty()) { + if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) { + String id; + switch (symbol->kind) { + case lsp::SymbolKind::Class: + id = "class_name:" + symbol->name; + break; + case lsp::SymbolKind::Constant: + id = "class_constant:" + symbol->native_class + ":" + symbol->name; + break; + case lsp::SymbolKind::Property: + case lsp::SymbolKind::Variable: + id = "class_property:" + symbol->native_class + ":" + symbol->name; + break; + case lsp::SymbolKind::Enum: + id = "class_enum:" + symbol->native_class + ":" + symbol->name; + break; + case lsp::SymbolKind::Method: + case lsp::SymbolKind::Function: + id = "class_method:" + symbol->native_class + ":" + symbol->name; + break; + default: + id = "class_global:" + symbol->native_class + ":" + symbol->name; + break; + } + call_deferred("show_native_symbol_in_editor", id); + } else { + notify_client_show_symbol(symbol); } - call_deferred("show_native_symbol_in_editor", id); } } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { @@ -380,8 +414,8 @@ GDScriptTextDocument::~GDScriptTextDocument() { memdelete(file_checker); } -void GDScriptTextDocument::sync_script_content(const String &p_uri, const String &p_content) { - String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_uri); +void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) { + String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path); GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); } diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index d15022d2c4..235e2c3f6e 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -52,14 +52,16 @@ protected: private: lsp::TextDocumentItem load_document_item(const Variant &p_param); + void notify_client_show_symbol(const lsp::DocumentSymbol *symbol); public: + Variant nativeSymbol(const Dictionary &p_params); Array documentSymbol(const Dictionary &p_params); Array completion(const Dictionary &p_params); Dictionary resolve(const Dictionary &p_params); Array foldingRange(const Dictionary &p_params); Array codeLens(const Dictionary &p_params); - Variant documentLink(const Dictionary &p_params); + Array documentLink(const Dictionary &p_params); Array colorPresentation(const Dictionary &p_params); Variant hover(const Dictionary &p_params); Array definition(const Dictionary &p_params); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 1901daacff..6baa7e4219 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -198,7 +198,7 @@ Error GDScriptWorkspace::initialize() { if (!class_data.inherits.empty()) { class_symbol.detail += " extends " + class_data.inherits; } - class_symbol.documentation = ExtendGDScriptParser::marked_documentation(class_data.brief_description) + "\n" + ExtendGDScriptParser::marked_documentation(class_data.description); + class_symbol.documentation = class_data.brief_description + "\n" + class_data.description; for (int i = 0; i < class_data.constants.size(); i++) { const DocData::ConstantDoc &const_data = class_data.constants[i]; @@ -211,7 +211,7 @@ Error GDScriptWorkspace::initialize() { symbol.detail += ": " + const_data.enumeration; } symbol.detail += " = " + const_data.value; - symbol.documentation = ExtendGDScriptParser::marked_documentation(const_data.description); + symbol.documentation = const_data.description; class_symbol.children.push_back(symbol); } @@ -232,7 +232,7 @@ Error GDScriptWorkspace::initialize() { } else { symbol.detail += ": " + data.type; } - symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description); + symbol.documentation = data.description; class_symbol.children.push_back(symbol); } @@ -270,7 +270,7 @@ Error GDScriptWorkspace::initialize() { } symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + data.return_type; - symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description); + symbol.documentation = data.description; class_symbol.children.push_back(symbol); } @@ -475,6 +475,33 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP } } +const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) { + + if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) { + const lsp::DocumentSymbol &symbol = E->get(); + if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) { + return &symbol; + } + + for (int i = 0; i < symbol.children.size(); ++i) { + if (symbol.children[i].name == p_params.symbol_name) { + return &(symbol.children[i]); + } + } + } + + return NULL; +} + +void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) { + if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) { + const List<lsp::DocumentLink> &links = parser->get_document_links(); + for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) { + r_list.push_back(E->get()); + } + } +} + Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) { Dictionary api; if (const ExtendGDScriptParser *parser = get_parse_successed_script(p_path)) { diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index adce169d4b..a416ae1075 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -53,7 +53,6 @@ protected: ExtendGDScriptParser *get_parse_successed_script(const String &p_path); ExtendGDScriptParser *get_parse_result(const String &p_path); - void strip_flat_symbols(const String &p_branch); void list_script_files(const String &p_root_dir, List<String> &r_files); public: @@ -81,7 +80,8 @@ public: const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_requred = false); void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list); - + const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params); + void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list); Dictionary generate_script_api(const String &p_path); GDScriptWorkspace(); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 3e57b6ee7e..61a0980c41 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -37,6 +37,9 @@ namespace lsp { typedef String DocumentUri; +/** Format BBCode documentation from DocData to markdown */ +static String marked_documentation(const String &p_bbcode); + /** * Text documents are identified using a URI. On the protocol level, URIs are passed as strings. */ @@ -199,6 +202,41 @@ struct TextDocumentPositionParams { } }; +struct DocumentLinkParams { + /** + * The document to provide document links for. + */ + TextDocumentIdentifier textDocument; + + _FORCE_INLINE_ void load(const Dictionary &p_params) { + textDocument.load(p_params["textDocument"]); + } +}; + +/** + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ +struct DocumentLink { + + /** + * The range this link applies to. + */ + Range range; + + /** + * The uri this link points to. If missing a resolve request is sent later. + */ + DocumentUri target; + + Dictionary to_json() const { + Dictionary dict; + dict["range"] = range.to_json(); + dict["target"] = target; + return dict; + } +}; + /** * A textual edit applicable to a text document. */ @@ -247,6 +285,9 @@ struct Command { } }; +// Use namespace instead of enumeration to follow the LSP specifications +// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not + namespace TextDocumentSyncKind { /** * Documents should not be synced at all. @@ -557,6 +598,7 @@ struct TextDocumentContentChangeEvent { } }; +// Use namespace instead of enumeration to follow the LSP specifications namespace DiagnosticSeverity { /** * Reports an error. @@ -657,6 +699,7 @@ struct Diagnostic { } }; +// Use namespace instead of enumeration to follow the LSP specifications /** * Describes the content type that a client supports in various * result literals like `Hover`, `ParameterInfo` or `CompletionItem`. @@ -721,6 +764,9 @@ struct MarkupContent { } }; +// Use namespace instead of enumeration to follow the LSP specifications +// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not +// And here C++ compilers are unhappy with our enumeration name like Color, File, Reference etc. /** * The kind of a completion entry. */ @@ -752,6 +798,7 @@ static const int Operator = 24; static const int TypeParameter = 25; }; // namespace CompletionItemKind +// Use namespace instead of enumeration to follow the LSP specifications /** * Defines whether the insert text in a completion item should be interpreted as * plain text or a snippet. @@ -944,36 +991,39 @@ struct CompletionList { Vector<CompletionItem> items; }; +// Use namespace instead of enumeration to follow the LSP specifications +// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not +// And here C++ compilers are unhappy with our enumeration name like String, Array, Object etc /** * A symbol kind. */ namespace SymbolKind { -static const int File = 1; -static const int Module = 2; -static const int Namespace = 3; -static const int Package = 4; -static const int Class = 5; -static const int Method = 6; -static const int Property = 7; -static const int Field = 8; -static const int Constructor = 9; -static const int Enum = 10; -static const int Interface = 11; -static const int Function = 12; -static const int Variable = 13; -static const int Constant = 14; -static const int String = 15; -static const int Number = 16; -static const int Boolean = 17; -static const int Array = 18; -static const int Object = 19; -static const int Key = 20; -static const int Null = 21; -static const int EnumMember = 22; -static const int Struct = 23; -static const int Event = 24; -static const int Operator = 25; -static const int TypeParameter = 26; +static const int File = 0; +static const int Module = 1; +static const int Namespace = 2; +static const int Package = 3; +static const int Class = 4; +static const int Method = 5; +static const int Property = 6; +static const int Field = 7; +static const int Constructor = 8; +static const int Enum = 9; +static const int Interface = 10; +static const int Function = 11; +static const int Variable = 12; +static const int Constant = 13; +static const int String = 14; +static const int Number = 15; +static const int Boolean = 16; +static const int Array = 17; +static const int Object = 18; +static const int Key = 19; +static const int Null = 20; +static const int EnumMember = 21; +static const int Struct = 22; +static const int Event = 23; +static const int Operator = 24; +static const int TypeParameter = 25; }; // namespace SymbolKind /** @@ -1099,7 +1149,7 @@ struct DocumentSymbol { */ Vector<DocumentSymbol> children; - _FORCE_INLINE_ Dictionary to_json() const { + Dictionary to_json(bool with_doc = false) const { Dictionary dict; dict["name"] = name; dict["detail"] = detail; @@ -1107,10 +1157,14 @@ struct DocumentSymbol { dict["deprecated"] = deprecated; dict["range"] = range.to_json(); dict["selectionRange"] = selectionRange.to_json(); + if (with_doc) { + dict["documentation"] = documentation; + dict["native_class"] = native_class; + } Array arr; arr.resize(children.size()); for (int i = 0; i < children.size(); i++) { - arr[i] = children[i].to_json(); + arr[i] = children[i].to_json(with_doc); } dict["children"] = arr; return dict; @@ -1142,7 +1196,7 @@ struct DocumentSymbol { markdown.value = "\t" + detail + "\n\n"; } if (documentation.length()) { - markdown.value += documentation + "\n\n"; + markdown.value += marked_documentation(documentation) + "\n\n"; } if (script_path.length()) { markdown.value += "Defined in [" + script_path + "](" + uri + ")"; @@ -1194,6 +1248,17 @@ struct DocumentSymbol { } }; +struct NativeSymbolInspectParams { + + String native_class; + String symbol_name; + + void load(const Dictionary &p_params) { + native_class = p_params["native_class"]; + symbol_name = p_params["symbol_name"]; + } +}; + /** * Enum of known range kinds */ @@ -1254,6 +1319,7 @@ struct FoldingRange { } }; +// Use namespace instead of enumeration to follow the LSP specifications /** * How a completion was triggered */ @@ -1501,6 +1567,61 @@ struct InitializeResult { } }; +/** Format BBCode documentation from DocData to markdown */ +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; + + markdown = ""; + for (int i = 0; i < lines.size(); i++) { + String line = lines[i]; + 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, line.length()); + } + + if (in_code_block && line.find("[/codeblock]") != -1) { + line = "\n"; + in_code_block = false; + } + + if (!in_code_block) { + line = line.strip_edges(); + line = line.replace("[code]", "`"); + line = line.replace("[/code]", "`"); + line = line.replace("[i]", "*"); + line = line.replace("[/i]", "*"); + line = line.replace("[b]", "**"); + 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("[", "`"); + line = line.replace("]", "`"); + } + + if (!in_code_block && i < lines.size() - 1) { + line += "\n\n"; + } else if (i < lines.size() - 1) { + line += "\n"; + } + markdown += line; + } + return markdown; +} + } // namespace lsp #endif |