Przeglądaj źródła

Add generate script api to dictionary support
Expose GDScriptLanguageProtocol singleton and classes for editor plugins (Not visiable in class tree)
Fix minor bug in symbol resolve

Geequlim 6 lat temu
rodzic
commit
666ed89011

+ 147 - 5
modules/gdscript/language_server/gdscript_extend_parser.cpp

@@ -139,7 +139,10 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 		symbol.range.end.line = line;
 		symbol.range.end.line = line;
 		symbol.range.end.character = lines[line].length();
 		symbol.range.end.character = lines[line].length();
 		symbol.selectionRange.start.line = symbol.range.start.line;
 		symbol.selectionRange.start.line = symbol.range.start.line;
-		symbol.detail = "var " + m.identifier;
+		if (m._export.type != Variant::NIL) {
+			symbol.detail += "export ";
+		}
+		symbol.detail += "var " + m.identifier;
 		if (m.data_type.kind != GDScriptParser::DataType::UNRESOLVED) {
 		if (m.data_type.kind != GDScriptParser::DataType::UNRESOLVED) {
 			symbol.detail += ": " + m.data_type.to_string();
 			symbol.detail += ": " + m.data_type.to_string();
 		}
 		}
@@ -210,7 +213,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 			if (res.is_valid() && !res->get_path().empty()) {
 			if (res.is_valid() && !res->get_path().empty()) {
 				value_text = "preload(\"" + res->get_path() + "\")";
 				value_text = "preload(\"" + res->get_path() + "\")";
 				if (symbol.documentation.empty()) {
 				if (symbol.documentation.empty()) {
-					if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(res->get_path())) {
+					if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
 						symbol.documentation = S->get()->class_symbol.documentation;
 						symbol.documentation = S->get()->class_symbol.documentation;
 					}
 					}
 				}
 				}
@@ -335,7 +338,7 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
 		String inline_comment = lines[p_line];
 		String inline_comment = lines[p_line];
 		int comment_start = inline_comment.find("#");
 		int comment_start = inline_comment.find("#");
 		if (comment_start != -1) {
 		if (comment_start != -1) {
-			inline_comment = inline_comment.substr(comment_start, inline_comment.length());
+			inline_comment = inline_comment.substr(comment_start, inline_comment.length()).strip_edges();
 			if (inline_comment.length() > 1) {
 			if (inline_comment.length() > 1) {
 				doc_lines.push_back(inline_comment.substr(1, inline_comment.length()));
 				doc_lines.push_back(inline_comment.substr(1, inline_comment.length()));
 			}
 			}
@@ -407,7 +410,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
 		if (i == p_cursor.line) {
 		if (i == p_cursor.line) {
 			String line = lines[i];
 			String line = lines[i];
 			String first_part = line.substr(0, p_cursor.character);
 			String first_part = line.substr(0, p_cursor.character);
-			String last_part = line.substr(p_cursor.character, lines[i].size());
+			String last_part = line.substr(p_cursor.character + 1, lines[i].length());
 			if (!p_symbol.empty()) {
 			if (!p_symbol.empty()) {
 				String left_cursor_text;
 				String left_cursor_text;
 				for (int c = p_cursor.character - 1; c >= 0; c--) {
 				for (int c = p_cursor.character - 1; c >= 0; c--) {
@@ -473,7 +476,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
 }
 }
 
 
 String ExtendGDScriptParser::get_uri() const {
 String ExtendGDScriptParser::get_uri() const {
-	return GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_uri(path);
+	return GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path);
 }
 }
 
 
 const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const {
 const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const {
@@ -555,6 +558,145 @@ const Array &ExtendGDScriptParser::get_member_completions() {
 	return member_completions;
 	return member_completions;
 }
 }
 
 
+Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::FunctionNode *p_func) const {
+	Dictionary func;
+	ERR_FAIL_NULL_V(p_func, func);
+	func["name"] = p_func->name;
+	func["return_type"] = p_func->return_type.to_string();
+	func["rpc_mode"] = p_func->rpc_mode;
+	Array arguments;
+	for (int i = 0; i < p_func->arguments.size(); i++) {
+		Dictionary arg;
+		arg["name"] = p_func->arguments[i];
+		arg["type"] = p_func->argument_types[i].to_string();
+		int default_value_idx = i - (p_func->arguments.size() - p_func->default_values.size());
+		if (default_value_idx >= 0) {
+			const GDScriptParser::ConstantNode *const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(p_func->default_values[default_value_idx]);
+			if (const_node == NULL) {
+				const GDScriptParser::OperatorNode *operator_node = dynamic_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[default_value_idx]);
+				if (operator_node) {
+					const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(operator_node->next);
+				}
+			}
+			if (const_node) {
+				arg["default_value"] = const_node->value;
+			}
+		}
+		arguments.push_back(arg);
+	}
+	if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_func->line))) {
+		func["signature"] = symbol->detail;
+		func["description"] = symbol->documentation;
+	}
+	func["arguments"] = arguments;
+	return func;
+}
+
+Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode *p_class) const {
+	Dictionary class_api;
+
+	ERR_FAIL_NULL_V(p_class, class_api);
+
+	class_api["name"] = String(p_class->name);
+	class_api["path"] = path;
+	Array extends_class;
+	for (int i = 0; i < p_class->extends_class.size(); i++) {
+		extends_class.append(String(p_class->extends_class[i]));
+	}
+	class_api["extends_class"] = extends_class;
+	class_api["extends_file"] = String(p_class->extends_file);
+	class_api["icon"] = String(p_class->icon_path);
+
+	if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_class->line))) {
+		class_api["signature"] = symbol->detail;
+		class_api["description"] = symbol->documentation;
+	}
+
+	Array subclasses;
+	for (int i = 0; i < p_class->subclasses.size(); i++) {
+		subclasses.push_back(dump_class_api(p_class->subclasses[i]));
+	}
+	class_api["sub_classes"] = subclasses;
+
+	Array constants;
+	for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
+
+		const GDScriptParser::ClassNode::Constant &c = E->value();
+		const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression);
+
+		Dictionary api;
+		api["name"] = E->key();
+		api["value"] = node->value;
+		api["data_type"] = node->datatype.to_string();
+		if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(node->line))) {
+			api["signature"] = symbol->detail;
+			api["description"] = symbol->documentation;
+		}
+		constants.push_back(api);
+	}
+	class_api["constants"] = constants;
+
+	Array members;
+	for (int i = 0; i < p_class->variables.size(); ++i) {
+		const GDScriptParser::ClassNode::Member &m = p_class->variables[i];
+		Dictionary api;
+		api["name"] = m.identifier;
+		api["data_type"] = m.data_type.to_string();
+		api["default_value"] = m.default_value;
+		api["setter"] = String(m.setter);
+		api["getter"] = String(m.getter);
+		api["export"] = m._export.type != Variant::NIL;
+		if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line))) {
+			api["signature"] = symbol->detail;
+			api["description"] = symbol->documentation;
+		}
+		members.push_back(api);
+	}
+	class_api["members"] = members;
+
+	Array signals;
+	for (int i = 0; i < p_class->_signals.size(); ++i) {
+		const GDScriptParser::ClassNode::Signal &signal = p_class->_signals[i];
+		Dictionary api;
+		api["name"] = signal.name;
+		Array args;
+		for (int j = 0; j < signal.arguments.size(); j++) {
+			args.append(signal.arguments[j]);
+		}
+		api["arguments"] = args;
+		if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(signal.line))) {
+			api["signature"] = symbol->detail;
+			api["description"] = symbol->documentation;
+		}
+		signals.push_back(api);
+	}
+	class_api["signals"] = signals;
+
+	Array methods;
+	for (int i = 0; i < p_class->functions.size(); ++i) {
+		methods.append(dump_function_api(p_class->functions[i]));
+	}
+	class_api["methods"] = methods;
+
+	Array static_functions;
+	for (int i = 0; i < p_class->static_functions.size(); ++i) {
+		static_functions.append(dump_function_api(p_class->functions[i]));
+	}
+	class_api["static_functions"] = static_functions;
+
+	return class_api;
+}
+
+Dictionary ExtendGDScriptParser::generate_api() const {
+
+	Dictionary api;
+	const GDScriptParser::Node *head = get_parse_tree();
+	if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
+		api = dump_class_api(gdclass);
+	}
+	return api;
+}
+
 Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
 Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
 	path = p_path;
 	path = p_path;
 	lines = p_code.split("\n");
 	lines = p_code.split("\n");

+ 4 - 0
modules/gdscript/language_server/gdscript_extend_parser.h

@@ -65,6 +65,9 @@ class ExtendGDScriptParser : public GDScriptParser {
 	void parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol);
 	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);
 	void parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol);
 
 
+	Dictionary dump_function_api(const GDScriptParser::FunctionNode *p_func) const;
+	Dictionary dump_class_api(const GDScriptParser::ClassNode *p_class) const;
+
 	String parse_documentation(int p_line, bool p_docs_down = false);
 	String parse_documentation(int p_line, bool p_docs_down = false);
 	const lsp::DocumentSymbol *search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const;
 	const lsp::DocumentSymbol *search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const;
 
 
@@ -87,6 +90,7 @@ public:
 	const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const;
 	const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const;
 
 
 	const Array &get_member_completions();
 	const Array &get_member_completions();
+	Dictionary generate_api() const;
 
 
 	Error parse(const String &p_code, const String &p_path);
 	Error parse(const String &p_code, const String &p_path);
 };
 };

+ 20 - 12
modules/gdscript/language_server/gdscript_language_protocol.cpp

@@ -86,6 +86,12 @@ void GDScriptLanguageProtocol::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("on_data_received"), &GDScriptLanguageProtocol::on_data_received);
 	ClassDB::bind_method(D_METHOD("on_data_received"), &GDScriptLanguageProtocol::on_data_received);
 	ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
 	ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
 	ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
 	ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
+	ClassDB::bind_method(D_METHOD("notify_all_clients", "p_method", "p_params"), &GDScriptLanguageProtocol::notify_all_clients, DEFVAL(Variant()));
+	ClassDB::bind_method(D_METHOD("notify_client", "p_method", "p_params", "p_client"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
+	ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
+	ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
+	ClassDB::bind_method(D_METHOD("is_initialized"), &GDScriptLanguageProtocol::is_initialized);
 }
 }
 
 
 Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
 Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
@@ -94,20 +100,20 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
 
 
 	String root_uri = p_params["rootUri"];
 	String root_uri = p_params["rootUri"];
 	String root = p_params["rootPath"];
 	String root = p_params["rootPath"];
-	bool is_same_workspace = root == workspace.root;
-	is_same_workspace = root.to_lower() == workspace.root.to_lower();
+	bool is_same_workspace = root == workspace->root;
+	is_same_workspace = root.to_lower() == workspace->root.to_lower();
 #ifdef WINDOWS_ENABLED
 #ifdef WINDOWS_ENABLED
-	is_same_workspace = root.replace("\\", "/").to_lower() == workspace.root.to_lower();
+	is_same_workspace = root.replace("\\", "/").to_lower() == workspace->root.to_lower();
 #endif
 #endif
 
 
 	if (root_uri.length() && is_same_workspace) {
 	if (root_uri.length() && is_same_workspace) {
-		workspace.root_uri = root_uri;
+		workspace->root_uri = root_uri;
 	} else {
 	} else {
 
 
-		workspace.root_uri = "file://" + workspace.root;
+		workspace->root_uri = "file://" + workspace->root;
 
 
 		Dictionary params;
 		Dictionary params;
-		params["path"] = workspace.root;
+		params["path"] = workspace->root;
 		Dictionary request = make_notification("gdscrip_client/changeWorkspace", params);
 		Dictionary request = make_notification("gdscrip_client/changeWorkspace", params);
 		if (Ref<WebSocketPeer> *peer = clients.getptr(lastest_client_id)) {
 		if (Ref<WebSocketPeer> *peer = clients.getptr(lastest_client_id)) {
 			String msg = JSON::print(request);
 			String msg = JSON::print(request);
@@ -118,8 +124,8 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
 	}
 	}
 
 
 	if (!_initialized) {
 	if (!_initialized) {
-		workspace.initialize();
-		text_document.initialize();
+		workspace->initialize();
+		text_document->initialize();
 		_initialized = true;
 		_initialized = true;
 	}
 	}
 
 
@@ -187,10 +193,12 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
 	server = NULL;
 	server = NULL;
 	singleton = this;
 	singleton = this;
 	_initialized = false;
 	_initialized = false;
-	set_scope("textDocument", &text_document);
-	set_scope("completionItem", &text_document);
-	set_scope("workspace", &workspace);
-	workspace.root = ProjectSettings::get_singleton()->get_resource_path();
+	workspace.instance();
+	text_document.instance();
+	set_scope("textDocument", text_document.ptr());
+	set_scope("completionItem", text_document.ptr());
+	set_scope("workspace", workspace.ptr());
+	workspace->root = ProjectSettings::get_singleton()->get_resource_path();
 }
 }
 
 
 GDScriptLanguageProtocol::~GDScriptLanguageProtocol() {
 GDScriptLanguageProtocol::~GDScriptLanguageProtocol() {

+ 5 - 3
modules/gdscript/language_server/gdscript_language_protocol.h

@@ -52,8 +52,8 @@ class GDScriptLanguageProtocol : public JSONRPC {
 	WebSocketServer *server;
 	WebSocketServer *server;
 	int lastest_client_id;
 	int lastest_client_id;
 
 
-	GDScriptTextDocument text_document;
-	GDScriptWorkspace workspace;
+	Ref<GDScriptTextDocument> text_document;
+	Ref<GDScriptWorkspace> workspace;
 
 
 	void on_data_received(int p_id);
 	void on_data_received(int p_id);
 	void on_client_connected(int p_id, const String &p_protocal);
 	void on_client_connected(int p_id, const String &p_protocal);
@@ -72,7 +72,9 @@ protected:
 
 
 public:
 public:
 	_FORCE_INLINE_ static GDScriptLanguageProtocol *get_singleton() { return singleton; }
 	_FORCE_INLINE_ static GDScriptLanguageProtocol *get_singleton() { return singleton; }
-	_FORCE_INLINE_ GDScriptWorkspace &get_workspace() { return workspace; }
+	_FORCE_INLINE_ Ref<GDScriptWorkspace> get_workspace() { return workspace; }
+	_FORCE_INLINE_ Ref<GDScriptTextDocument> get_text_document() { return text_document; }
+	_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
 
 
 	void poll();
 	void poll();
 	Error start(int p_port);
 	Error start(int p_port);

+ 15 - 15
modules/gdscript/language_server/gdscript_text_document.cpp

@@ -77,7 +77,7 @@ void GDScriptTextDocument::initialize() {
 
 
 	if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
 	if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
 
 
-		const HashMap<StringName, ClassMembers> &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace().native_members;
+		const HashMap<StringName, ClassMembers> &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members;
 
 
 		const StringName *class_ptr = native_members.next(NULL);
 		const StringName *class_ptr = native_members.next(NULL);
 		while (class_ptr) {
 		while (class_ptr) {
@@ -103,9 +103,9 @@ void GDScriptTextDocument::initialize() {
 Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
 Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
 	Dictionary params = p_params["textDocument"];
 	Dictionary params = p_params["textDocument"];
 	String uri = params["uri"];
 	String uri = params["uri"];
-	String path = GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_path(uri);
+	String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri);
 	Array arr;
 	Array arr;
-	if (const Map<String, ExtendGDScriptParser *>::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(path)) {
+	if (const Map<String, ExtendGDScriptParser *>::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) {
 		Vector<lsp::DocumentedSymbolInformation> list;
 		Vector<lsp::DocumentedSymbolInformation> list;
 		parser->get()->get_symbols().symbol_tree_as_list(uri, list);
 		parser->get()->get_symbols().symbol_tree_as_list(uri, list);
 		for (int i = 0; i < list.size(); i++) {
 		for (int i = 0; i < list.size(); i++) {
@@ -124,7 +124,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
 	Dictionary request_data = params.to_json();
 	Dictionary request_data = params.to_json();
 
 
 	List<ScriptCodeCompletionOption> options;
 	List<ScriptCodeCompletionOption> options;
-	GDScriptLanguageProtocol::get_singleton()->get_workspace().completion(params, &options);
+	GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options);
 
 
 	if (!options.empty()) {
 	if (!options.empty()) {
 
 
@@ -178,7 +178,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
 
 
 		arr = native_member_completions.duplicate();
 		arr = native_member_completions.duplicate();
 
 
-		for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.front(); E; E = E->next()) {
+		for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.front(); E; E = E->next()) {
 
 
 			ExtendGDScriptParser *script = E->get();
 			ExtendGDScriptParser *script = E->get();
 			const Array &items = script->get_member_completions();
 			const Array &items = script->get_member_completions();
@@ -206,7 +206,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
 	if (data.get_type() == Variant::DICTIONARY) {
 	if (data.get_type() == Variant::DICTIONARY) {
 
 
 		params.load(p_params["data"]);
 		params.load(p_params["data"]);
-		symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function);
+		symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function);
 
 
 	} else if (data.get_type() == Variant::STRING) {
 	} else if (data.get_type() == Variant::STRING) {
 
 
@@ -224,14 +224,14 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
 				inner_class_name = param_symbols[1];
 				inner_class_name = param_symbols[1];
 			}
 			}
 
 
-			if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace().native_members.getptr(class_name)) {
+			if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members.getptr(class_name)) {
 				if (const lsp::DocumentSymbol *const *member = members->getptr(member_name)) {
 				if (const lsp::DocumentSymbol *const *member = members->getptr(member_name)) {
 					symbol = *member;
 					symbol = *member;
 				}
 				}
 			}
 			}
 
 
 			if (!symbol) {
 			if (!symbol) {
-				if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(class_name)) {
+				if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) {
 					symbol = E->get()->get_member_symbol(member_name, inner_class_name);
 					symbol = E->get()->get_member_symbol(member_name, inner_class_name);
 				}
 				}
 			}
 			}
@@ -284,7 +284,7 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
 	lsp::TextDocumentPositionParams params;
 	lsp::TextDocumentPositionParams params;
 	params.load(p_params);
 	params.load(p_params);
 
 
-	const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params);
+	const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params);
 	if (symbol) {
 	if (symbol) {
 
 
 		lsp::Hover hover;
 		lsp::Hover hover;
@@ -296,7 +296,7 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
 		Dictionary ret;
 		Dictionary ret;
 		Array contents;
 		Array contents;
 		List<const lsp::DocumentSymbol *> list;
 		List<const lsp::DocumentSymbol *> list;
-		GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list);
+		GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list);
 		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
 		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
 			if (const lsp::DocumentSymbol *s = E->get()) {
 			if (const lsp::DocumentSymbol *s = E->get()) {
 				contents.push_back(s->render().value);
 				contents.push_back(s->render().value);
@@ -315,20 +315,20 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) {
 	lsp::TextDocumentPositionParams params;
 	lsp::TextDocumentPositionParams params;
 	params.load(p_params);
 	params.load(p_params);
 
 
-	const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params);
+	const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params);
 	if (symbol) {
 	if (symbol) {
 		lsp::Location location;
 		lsp::Location location;
 		location.uri = symbol->uri;
 		location.uri = symbol->uri;
 		location.range = symbol->range;
 		location.range = symbol->range;
 
 
-		const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_path(symbol->uri);
+		const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri);
 		if (file_checker->file_exists(path)) {
 		if (file_checker->file_exists(path)) {
 			arr.push_back(location.to_json());
 			arr.push_back(location.to_json());
 		}
 		}
 	} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
 	} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
 
 
 		List<const lsp::DocumentSymbol *> list;
 		List<const lsp::DocumentSymbol *> list;
-		GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list);
+		GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list);
 		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
 		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
 
 
 			if (const lsp::DocumentSymbol *s = E->get()) {
 			if (const lsp::DocumentSymbol *s = E->get()) {
@@ -354,6 +354,6 @@ GDScriptTextDocument::~GDScriptTextDocument() {
 }
 }
 
 
 void GDScriptTextDocument::sync_script_content(const String &p_uri, const String &p_content) {
 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);
-	GDScriptLanguageProtocol::get_singleton()->get_workspace().parse_script(path, p_content);
+	String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_uri);
+	GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
 }
 }

+ 14 - 0
modules/gdscript/language_server/gdscript_workspace.cpp

@@ -38,6 +38,12 @@
 
 
 void GDScriptWorkspace::_bind_methods() {
 void GDScriptWorkspace::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
 	ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
+	ClassDB::bind_method(D_METHOD("parse_script", "p_path", "p_content"), &GDScriptWorkspace::parse_script);
+	ClassDB::bind_method(D_METHOD("parse_local_script", "p_path"), &GDScriptWorkspace::parse_local_script);
+	ClassDB::bind_method(D_METHOD("get_file_path", "p_uri"), &GDScriptWorkspace::get_file_path);
+	ClassDB::bind_method(D_METHOD("get_file_uri", "p_path"), &GDScriptWorkspace::get_file_uri);
+	ClassDB::bind_method(D_METHOD("publish_diagnostics", "p_path"), &GDScriptWorkspace::publish_diagnostics);
+	ClassDB::bind_method(D_METHOD("generate_script_api", "p_path"), &GDScriptWorkspace::generate_script_api);
 }
 }
 
 
 void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
 void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
@@ -512,6 +518,14 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
 	}
 	}
 }
 }
 
 
+Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) {
+	Dictionary api;
+	if (const ExtendGDScriptParser *parser = get_parse_successed_script(p_path)) {
+		api = parser->generate_api();
+	}
+	return api;
+}
+
 GDScriptWorkspace::GDScriptWorkspace() {
 GDScriptWorkspace::GDScriptWorkspace() {
 	ProjectSettings::get_singleton()->get_resource_path();
 	ProjectSettings::get_singleton()->get_resource_path();
 }
 }

+ 2 - 0
modules/gdscript/language_server/gdscript_workspace.h

@@ -82,6 +82,8 @@ public:
 	const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_requred = false);
 	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);
 	void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list);
 
 
+	Dictionary generate_script_api(const String &p_path);
+
 	static String marked_documentation(const String &p_bbcode);
 	static String marked_documentation(const String &p_bbcode);
 
 
 	GDScriptWorkspace();
 	GDScriptWorkspace();

+ 5 - 1
modules/gdscript/register_types.cpp

@@ -44,6 +44,7 @@ Ref<ResourceFormatSaverGDScript> resource_saver_gd;
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 
 
+#include "core/engine.h"
 #include "editor/editor_export.h"
 #include "editor/editor_export.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
@@ -131,8 +132,11 @@ static void _editor_init() {
 	Ref<EditorExportGDScript> gd_export;
 	Ref<EditorExportGDScript> gd_export;
 	gd_export.instance();
 	gd_export.instance();
 	EditorExport::get_singleton()->add_export_plugin(gd_export);
 	EditorExport::get_singleton()->add_export_plugin(gd_export);
-	EditorNode::get_singleton()->add_editor_plugin(memnew(GDScriptLanguageServer));
+
 	register_lsp_types();
 	register_lsp_types();
+	GDScriptLanguageServer *lsp_plugin = memnew(GDScriptLanguageServer);
+	EditorNode::get_singleton()->add_editor_plugin(lsp_plugin);
+	Engine::get_singleton()->add_singleton(Engine::Singleton("GDScriptLanguageProtocol", GDScriptLanguageProtocol::get_singleton()));
 }
 }
 
 
 #endif
 #endif