Parcourir la source

Add optional smart resolve sulotion
The smart resolvaion can guess most symbols but it might be slow so disabled by default users can turn on it in the editor setting

geequlim il y a 6 ans
Parent
commit
fa6d6a329c

+ 36 - 7
modules/gdscript/language_server/gdscript_extend_parser.cpp

@@ -123,7 +123,9 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 		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();
 		}
 		}
-		symbol.detail += " = " + String(m.default_value);
+		if (m.default_value.get_type() != Variant::NIL) {
+			symbol.detail += " = " + JSON::print(m.default_value);
+		}
 
 
 		symbol.documentation = parse_documentation(line);
 		symbol.documentation = parse_documentation(line);
 		symbol.uri = uri;
 		symbol.uri = uri;
@@ -493,12 +495,39 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String
 	return NULL;
 	return NULL;
 }
 }
 
 
-void ExtendGDScriptParser::dump_symbols(HashMap<String, lsp::DocumentedSymbolInformation> &r_symbols) {
-	Vector<lsp::DocumentedSymbolInformation> list;
-	class_symbol.symbol_tree_as_list(path, list, path, true);
-	for (int i = 0; i < list.size(); i++) {
-		const lsp::DocumentedSymbolInformation &symbol = list[i];
-		r_symbols.set(symbol.name, symbol);
+void ExtendGDScriptParser::dump_member_symbols(Map<String, const lsp::DocumentSymbol *> &r_symbols) {
+
+	const GDScriptParser::Node *head = get_parse_tree();
+	if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
+
+		for (const Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = gdclass->constant_expressions.front(); E; E = E->next()) {
+			get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(E->get().expression->line));
+		}
+
+		for (int i = 0; i < gdclass->subclasses.size(); i++) {
+			const ClassNode *m = gdclass->subclasses[i];
+			r_symbols.insert(JOIN_SYMBOLS(path, m->name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line)));
+		}
+
+		for (int i = 0; i < gdclass->variables.size(); i++) {
+			const GDScriptParser::ClassNode::Member &m = gdclass->variables[i];
+			r_symbols.insert(JOIN_SYMBOLS(path, m.identifier), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line)));
+		}
+
+		for (int i = 0; i < gdclass->functions.size(); i++) {
+			const GDScriptParser::FunctionNode *m = gdclass->functions[i];
+			r_symbols.insert(JOIN_SYMBOLS(path, m->name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line)));
+		}
+
+		for (int i = 0; i < gdclass->static_functions.size(); i++) {
+			const GDScriptParser::FunctionNode *m = gdclass->static_functions[i];
+			r_symbols.insert(JOIN_SYMBOLS(path, m->name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line)));
+		}
+
+		for (int i = 0; i < gdclass->_signals.size(); i++) {
+			const GDScriptParser::ClassNode::Signal &m = gdclass->_signals[i];
+			r_symbols.insert(JOIN_SYMBOLS(path, m.name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line)));
+		}
 	}
 	}
 }
 }
 
 

+ 5 - 2
modules/gdscript/language_server/gdscript_extend_parser.h

@@ -39,6 +39,10 @@
 #define LINE_NUMBER_TO_INDEX(p_line) ((p_line)-1)
 #define LINE_NUMBER_TO_INDEX(p_line) ((p_line)-1)
 #endif
 #endif
 
 
+#ifndef JOIN_SYMBOLS
+#define JOIN_SYMBOLS(p_path, name) ((p_path) + "." + (name))
+#endif
+
 class ExtendGDScriptParser : public GDScriptParser {
 class ExtendGDScriptParser : public GDScriptParser {
 	String path;
 	String path;
 	String code;
 	String code;
@@ -70,8 +74,7 @@ public:
 
 
 	const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line) const;
 	const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line) const;
 	const lsp::DocumentSymbol *get_member_symbol(const String &p_name) const;
 	const lsp::DocumentSymbol *get_member_symbol(const String &p_name) const;
-
-	void dump_symbols(HashMap<String, lsp::DocumentedSymbolInformation> &r_symbols);
+	void dump_member_symbols(Map<String, const lsp::DocumentSymbol *> &r_symbols);
 
 
 	Error parse(const String &p_code, const String &p_path);
 	Error parse(const String &p_code, const String &p_path);
 };
 };

+ 5 - 0
modules/gdscript/language_server/gdscript_language_protocol.cpp

@@ -32,6 +32,7 @@
 #include "core/io/json.h"
 #include "core/io/json.h"
 #include "core/os/copymem.h"
 #include "core/os/copymem.h"
 #include "core/project_settings.h"
 #include "core/project_settings.h"
+#include "editor/editor_node.h"
 
 
 GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = NULL;
 GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = NULL;
 
 
@@ -159,6 +160,10 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
 	(*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
 	(*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
 }
 }
 
 
+bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const {
+	return bool(_EDITOR_GET("network/language_server/enable_smart_resolve"));
+}
+
 GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
 GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
 	server = NULL;
 	server = NULL;
 	singleton = this;
 	singleton = this;

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

@@ -79,6 +79,8 @@ public:
 	void notify_all_clients(const String &p_method, const Variant &p_params = Variant());
 	void notify_all_clients(const String &p_method, const Variant &p_params = Variant());
 	void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client = -1);
 	void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client = -1);
 
 
+	bool is_smart_resolve_enabled() const;
+
 	GDScriptLanguageProtocol();
 	GDScriptLanguageProtocol();
 	~GDScriptLanguageProtocol();
 	~GDScriptLanguageProtocol();
 };
 };

+ 1 - 0
modules/gdscript/language_server/gdscript_language_server.cpp

@@ -37,6 +37,7 @@ GDScriptLanguageServer::GDScriptLanguageServer() {
 	thread = NULL;
 	thread = NULL;
 	thread_exit = false;
 	thread_exit = false;
 	_EDITOR_DEF("network/language_server/remote_port", 6008);
 	_EDITOR_DEF("network/language_server/remote_port", 6008);
+	_EDITOR_DEF("network/language_server/enable_smart_resolve", false);
 }
 }
 
 
 void GDScriptLanguageServer::_notification(int p_what) {
 void GDScriptLanguageServer::_notification(int p_what) {

+ 136 - 48
modules/gdscript/language_server/gdscript_text_document.cpp

@@ -68,7 +68,6 @@ lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_
 	lsp::TextDocumentItem doc;
 	lsp::TextDocumentItem doc;
 	Dictionary params = p_param;
 	Dictionary params = p_param;
 	doc.load(params["textDocument"]);
 	doc.load(params["textDocument"]);
-	print_line(doc.text);
 	return doc;
 	return doc;
 }
 }
 
 
@@ -97,62 +96,122 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
 	List<ScriptCodeCompletionOption> options;
 	List<ScriptCodeCompletionOption> options;
 	GDScriptLanguageProtocol::get_singleton()->get_workspace().completion(params, &options);
 	GDScriptLanguageProtocol::get_singleton()->get_workspace().completion(params, &options);
 
 
-	for (const List<ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) {
-		const ScriptCodeCompletionOption &option = E->get();
-		lsp::CompletionItem item;
-		item.label = option.display;
-		item.insertText = option.insert_text;
-		item.data = request_data;
+	if (!options.empty()) {
 
 
-		if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "'" || params.context.triggerCharacter == "\"") && (option.insert_text.begins_with("'") || option.insert_text.begins_with("\""))) {
-			item.insertText = option.insert_text.substr(1, option.insert_text.length() - 2);
-		}
+		for (const List<ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) {
+
+			const ScriptCodeCompletionOption &option = E->get();
+			lsp::CompletionItem item;
+			item.label = option.display;
+			item.insertText = option.insert_text;
+			item.data = request_data;
 
 
-		switch (option.kind) {
-			case ScriptCodeCompletionOption::KIND_ENUM:
-				item.kind = lsp::CompletionItemKind::Enum;
-				break;
-			case ScriptCodeCompletionOption::KIND_CLASS:
-				item.kind = lsp::CompletionItemKind::Class;
-				break;
-			case ScriptCodeCompletionOption::KIND_MEMBER:
-				item.kind = lsp::CompletionItemKind::Property;
-				break;
-			case ScriptCodeCompletionOption::KIND_FUNCTION:
-				item.kind = lsp::CompletionItemKind::Method;
-				break;
-			case ScriptCodeCompletionOption::KIND_SIGNAL:
-				item.kind = lsp::CompletionItemKind::Event;
-				break;
-			case ScriptCodeCompletionOption::KIND_CONSTANT:
-				item.kind = lsp::CompletionItemKind::Constant;
-				break;
-			case ScriptCodeCompletionOption::KIND_VARIABLE:
-				item.kind = lsp::CompletionItemKind::Variable;
-				break;
-			case ScriptCodeCompletionOption::KIND_FILE_PATH:
-				item.kind = lsp::CompletionItemKind::File;
-				break;
-			case ScriptCodeCompletionOption::KIND_NODE_PATH:
-				item.kind = lsp::CompletionItemKind::Snippet;
-				break;
-			case ScriptCodeCompletionOption::KIND_PLAIN_TEXT:
-				item.kind = lsp::CompletionItemKind::Text;
-				break;
+			if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "'" || params.context.triggerCharacter == "\"") && (option.insert_text.begins_with("'") || option.insert_text.begins_with("\""))) {
+				item.insertText = option.insert_text.substr(1, option.insert_text.length() - 2);
+			}
+
+			switch (option.kind) {
+				case ScriptCodeCompletionOption::KIND_ENUM:
+					item.kind = lsp::CompletionItemKind::Enum;
+					break;
+				case ScriptCodeCompletionOption::KIND_CLASS:
+					item.kind = lsp::CompletionItemKind::Class;
+					break;
+				case ScriptCodeCompletionOption::KIND_MEMBER:
+					item.kind = lsp::CompletionItemKind::Property;
+					break;
+				case ScriptCodeCompletionOption::KIND_FUNCTION:
+					item.kind = lsp::CompletionItemKind::Method;
+					break;
+				case ScriptCodeCompletionOption::KIND_SIGNAL:
+					item.kind = lsp::CompletionItemKind::Event;
+					break;
+				case ScriptCodeCompletionOption::KIND_CONSTANT:
+					item.kind = lsp::CompletionItemKind::Constant;
+					break;
+				case ScriptCodeCompletionOption::KIND_VARIABLE:
+					item.kind = lsp::CompletionItemKind::Variable;
+					break;
+				case ScriptCodeCompletionOption::KIND_FILE_PATH:
+					item.kind = lsp::CompletionItemKind::File;
+					break;
+				case ScriptCodeCompletionOption::KIND_NODE_PATH:
+					item.kind = lsp::CompletionItemKind::Snippet;
+					break;
+				case ScriptCodeCompletionOption::KIND_PLAIN_TEXT:
+					item.kind = lsp::CompletionItemKind::Text;
+					break;
+			}
+			arr.push_back(item.to_json());
 		}
 		}
 
 
-		arr.push_back(item.to_json());
-	}
+	} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
 
 
+		for (Map<String, const lsp::DocumentSymbol *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().flat_symbols.front(); E; E = E->next()) {
+			const lsp::DocumentSymbol *symbol = E->get();
+			if (!symbol) continue;
+
+			lsp::CompletionItem item;
+			item.label = symbol->name;
+			item.data = E->key();
+
+			switch (symbol->kind) {
+				case lsp::SymbolKind::Enum:
+					item.kind = lsp::CompletionItemKind::Enum;
+					break;
+				case lsp::SymbolKind::Class:
+					item.kind = lsp::CompletionItemKind::Class;
+					break;
+				case lsp::SymbolKind::Property:
+					item.kind = lsp::CompletionItemKind::Property;
+					break;
+				case lsp::SymbolKind::Method:
+				case lsp::SymbolKind::Function:
+					item.kind = lsp::CompletionItemKind::Method;
+					break;
+				case lsp::SymbolKind::Event:
+					item.kind = lsp::CompletionItemKind::Event;
+					break;
+				case lsp::SymbolKind::Constant:
+					item.kind = lsp::CompletionItemKind::Constant;
+					break;
+				case lsp::SymbolKind::Variable:
+					item.kind = lsp::CompletionItemKind::Variable;
+					break;
+				case lsp::SymbolKind::File:
+					item.kind = lsp::CompletionItemKind::File;
+					break;
+				default:
+					item.kind = lsp::CompletionItemKind::Text;
+					break;
+			}
+			arr.push_back(item.to_json());
+		}
+	}
 	return arr;
 	return arr;
 }
 }
 
 
 Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
 Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
+
 	lsp::CompletionItem item;
 	lsp::CompletionItem item;
 	item.load(p_params);
 	item.load(p_params);
+
 	lsp::CompletionParams params;
 	lsp::CompletionParams params;
-	params.load(p_params["data"]);
-	const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function);
+	Variant data = p_params["data"];
+
+	const lsp::DocumentSymbol *symbol = NULL;
+
+	if (data.get_type() == Variant::DICTIONARY) {
+		params.load(p_params["data"]);
+		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) {
+
+		if (Map<String, const lsp::DocumentSymbol *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().flat_symbols.find(data)) {
+			symbol = E->get();
+		}
+	}
+
 	if (symbol) {
 	if (symbol) {
 		item.documentation = symbol->render();
 		item.documentation = symbol->render();
 	}
 	}
@@ -182,7 +241,6 @@ Array GDScriptTextDocument::colorPresentation(const Dictionary &p_params) {
 }
 }
 
 
 Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
 Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
-	Variant ret;
 
 
 	lsp::TextDocumentPositionParams params;
 	lsp::TextDocumentPositionParams params;
 	params.load(p_params);
 	params.load(p_params);
@@ -191,10 +249,22 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
 	if (symbol) {
 	if (symbol) {
 		lsp::Hover hover;
 		lsp::Hover hover;
 		hover.contents = symbol->render();
 		hover.contents = symbol->render();
-		ret = hover.to_json();
+		return hover.to_json();
+	} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+		Dictionary ret;
+		Array contents;
+		List<const lsp::DocumentSymbol *> list;
+		GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list);
+		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
+			if (const lsp::DocumentSymbol *symbol = E->get()) {
+				contents.push_back(symbol->render().value);
+			}
+		}
+		ret["contents"] = contents;
+		return ret;
 	}
 	}
 
 
-	return ret;
+	return Variant();
 }
 }
 
 
 Array GDScriptTextDocument::definition(const Dictionary &p_params) {
 Array GDScriptTextDocument::definition(const Dictionary &p_params) {
@@ -213,6 +283,24 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) {
 		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()) {
+
+		List<const lsp::DocumentSymbol *> list;
+		GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list);
+		for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
+
+			if (const lsp::DocumentSymbol *symbol = E->get()) {
+
+				lsp::Location location;
+				location.uri = symbol->uri;
+				location.range = symbol->range;
+
+				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());
+				}
+			}
+		}
 	}
 	}
 
 
 	return arr;
 	return arr;

+ 64 - 7
modules/gdscript/language_server/gdscript_workspace.cpp

@@ -148,13 +148,10 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String
 }
 }
 
 
 ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) {
 ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) {
-	const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path);
+	const Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(p_path);
 	if (!S) {
 	if (!S) {
+		parse_local_script(p_path);
 		S = parse_results.find(p_path);
 		S = parse_results.find(p_path);
-		if (!S) {
-			parse_local_script(p_path);
-			S = scripts.find(p_path);
-		}
 	}
 	}
 	if (S) {
 	if (S) {
 		return S->get();
 		return S->get();
@@ -162,6 +159,22 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path)
 	return NULL;
 	return NULL;
 }
 }
 
 
+void GDScriptWorkspace::strip_flat_symbols(const String &p_branch) {
+
+	typedef Map<String, const lsp::DocumentSymbol *>::Element *Item;
+
+	List<Item> removal_items;
+	for (Item E = flat_symbols.front(); E; E = E->next()) {
+		if (E->key().begins_with(p_branch)) {
+			removal_items.push_back(E);
+		}
+	}
+
+	for (List<Item>::Element *E = removal_items.front(); E; E = E->next()) {
+		flat_symbols.erase(E->get());
+	}
+}
+
 String GDScriptWorkspace::marked_documentation(const String &p_bbcode) {
 String GDScriptWorkspace::marked_documentation(const String &p_bbcode) {
 
 
 	String markdown = p_bbcode.strip_edges();
 	String markdown = p_bbcode.strip_edges();
@@ -313,21 +326,41 @@ Error GDScriptWorkspace::initialize() {
 		native_symbols.insert(class_name, class_symbol);
 		native_symbols.insert(class_name, class_symbol);
 	}
 	}
 
 
+	if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+		// expand symbol trees to the flat symbol pool
+		for (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.front(); E; E = E->next()) {
+			const lsp::DocumentSymbol &class_symbol = E->get();
+			for (int i = 0; i < class_symbol.children.size(); i++) {
+				const lsp::DocumentSymbol &symbol = class_symbol.children[i];
+				flat_symbols.insert(JOIN_SYMBOLS(class_symbol.name, symbol.name), &symbol);
+			}
+		}
+	}
+
 	reload_all_workspace_scripts();
 	reload_all_workspace_scripts();
 
 
 	return OK;
 	return OK;
 }
 }
 
 
 Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) {
 Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) {
+
 	ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser);
 	ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser);
 	Error err = parser->parse(p_content, p_path);
 	Error err = parser->parse(p_content, p_path);
 	Map<String, ExtendGDScriptParser *>::Element *last_parser = parse_results.find(p_path);
 	Map<String, ExtendGDScriptParser *>::Element *last_parser = parse_results.find(p_path);
 	Map<String, ExtendGDScriptParser *>::Element *last_script = scripts.find(p_path);
 	Map<String, ExtendGDScriptParser *>::Element *last_script = scripts.find(p_path);
 
 
 	if (err == OK) {
 	if (err == OK) {
+
 		remove_cache_parser(p_path);
 		remove_cache_parser(p_path);
 		parse_results[p_path] = parser;
 		parse_results[p_path] = parser;
 		scripts[p_path] = parser;
 		scripts[p_path] = parser;
+
+		if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+			// update flat symbol pool
+			strip_flat_symbols(p_path);
+			parser->dump_member_symbols(flat_symbols);
+		}
+
 	} else {
 	} else {
 		if (last_parser && last_script && last_parser->get() != last_script->get()) {
 		if (last_parser && last_script && last_parser->get() != last_script->get()) {
 			memdelete(last_parser->get());
 			memdelete(last_parser->get());
@@ -377,11 +410,13 @@ void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
 }
 }
 
 
 void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options) {
 void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options) {
+
 	String path = get_file_path(p_params.textDocument.uri);
 	String path = get_file_path(p_params.textDocument.uri);
 	String call_hint;
 	String call_hint;
 	bool forced = false;
 	bool forced = false;
-	if (Map<String, ExtendGDScriptParser *>::Element *E = parse_results.find(path)) {
-		String code = E->get()->get_text_for_completion(p_params.position);
+
+	if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
+		String code = parser->get_text_for_completion(p_params.position);
 		GDScriptLanguage::get_singleton()->complete_code(code, path, NULL, r_options, forced, call_hint);
 		GDScriptLanguage::get_singleton()->complete_code(code, path, NULL, r_options, forced, call_hint);
 	}
 	}
 }
 }
@@ -442,6 +477,28 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
 	return symbol;
 	return symbol;
 }
 }
 
 
+void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list) {
+
+	String path = get_file_path(p_doc_pos.textDocument.uri);
+	if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
+
+		String symbol_identifier;
+		Vector2i offset;
+		symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
+
+		for (Map<String, const lsp::DocumentSymbol *>::Element *E = flat_symbols.front(); E; E = E->next()) {
+			String id = E->key();
+			int idx = id.find_last(".");
+			if (idx >= 0 && idx < id.length() - 1) {
+				String name = id.substr(idx + 1, id.length());
+				if (name == symbol_identifier) {
+					r_list.push_back(E->get());
+				}
+			}
+		}
+	}
+}
+
 GDScriptWorkspace::GDScriptWorkspace() {
 GDScriptWorkspace::GDScriptWorkspace() {
 	ProjectSettings::get_singleton()->get_resource_path();
 	ProjectSettings::get_singleton()->get_resource_path();
 }
 }

+ 11 - 1
modules/gdscript/language_server/gdscript_workspace.h

@@ -50,27 +50,37 @@ protected:
 
 
 	void reload_all_workspace_scripts();
 	void reload_all_workspace_scripts();
 
 
-	void list_script_files(const String &p_root_dir, List<String> &r_files);
 	ExtendGDScriptParser *get_parse_successed_script(const String &p_path);
 	ExtendGDScriptParser *get_parse_successed_script(const String &p_path);
 	ExtendGDScriptParser *get_parse_result(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:
 public:
 	String root;
 	String root;
+
 	Map<String, ExtendGDScriptParser *> scripts;
 	Map<String, ExtendGDScriptParser *> scripts;
 	Map<String, ExtendGDScriptParser *> parse_results;
 	Map<String, ExtendGDScriptParser *> parse_results;
+	Map<String, const lsp::DocumentSymbol *> flat_symbols;
 
 
 public:
 public:
 	Array symbol(const Dictionary &p_params);
 	Array symbol(const Dictionary &p_params);
 
 
 public:
 public:
 	Error initialize();
 	Error initialize();
+
 	Error parse_script(const String &p_path, const String &p_content);
 	Error parse_script(const String &p_path, const String &p_content);
 	Error parse_local_script(const String &p_path);
 	Error parse_local_script(const String &p_path);
+
 	String get_file_path(const String &p_uri) const;
 	String get_file_path(const String &p_uri) const;
 	String get_file_uri(const String &p_path) const;
 	String get_file_uri(const String &p_path) const;
+
 	void publish_diagnostics(const String &p_path);
 	void publish_diagnostics(const String &p_path);
 	void completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options);
 	void completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options);
+
 	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);
+
 	static String marked_documentation(const String &p_bbcode);
 	static String marked_documentation(const String &p_bbcode);
 
 
 	GDScriptWorkspace();
 	GDScriptWorkspace();