diff options
Diffstat (limited to 'modules/gdscript/language_server')
8 files changed, 139 insertions, 34 deletions
| diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index d106b3b541..f4c0c4d9bb 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -165,7 +165,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p  			case ClassNode::Member::VARIABLE: {  				lsp::DocumentSymbol symbol;  				symbol.name = m.variable->identifier->name; -				symbol.kind = m.variable->property == VariableNode::PropertyStyle::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property; +				symbol.kind = m.variable->property == VariableNode::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property;  				symbol.deprecated = false;  				symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line);  				symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column); @@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_text_for_completion(const lsp::Position &p_curs  	return longthing;  } -String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_requred) const { +String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_required) const {  	String longthing;  	int len = lines.size();  	for (int i = 0; i < len; i++) { @@ -513,7 +513,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c  			longthing += first_part;  			longthing += String::chr(0xFFFF); //not unicode, represents the cursor -			if (p_func_requred) { +			if (p_func_required) {  				longthing += "("; // tell the parser this is a function call  			}  			longthing += last_part; @@ -532,6 +532,9 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c  String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const {  	ERR_FAIL_INDEX_V(p_position.line, lines.size(), "");  	String line = lines[p_position.line]; +	if (line.is_empty()) { +		return ""; +	}  	ERR_FAIL_INDEX_V(p_position.character, line.size(), "");  	int start_pos = p_position.character; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index 28b9b3c82a..5d7b16765b 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -85,7 +85,7 @@ public:  	Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const;  	String get_text_for_completion(const lsp::Position &p_cursor) const; -	String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const; +	String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_required = false) const;  	String get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const;  	String get_uri() const; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index b6c48468f5..5cf1e0fc5f 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -212,11 +212,11 @@ void GDScriptLanguageProtocol::initialized(const Variant &p_params) {  	lsp::GodotCapabilities capabilities;  	DocTools *doc = EditorHelp::get_doc_data(); -	for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { +	for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {  		lsp::GodotNativeClassInfo gdclass; -		gdclass.name = E->get().name; -		gdclass.class_doc = &(E->get()); -		if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E->get().name))) { +		gdclass.name = E.value.name; +		gdclass.class_doc = &(E.value); +		if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E.value.name))) {  			gdclass.class_info = ptr;  		}  		capabilities.native_classes.push_back(gdclass); @@ -284,6 +284,23 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia  	peer->res_queue.push_back(msg.utf8());  } +void GDScriptLanguageProtocol::request_client(const String &p_method, const Variant &p_params, int p_client_id) { +	if (p_client_id == -1) { +		ERR_FAIL_COND_MSG(latest_client_id == -1, +				"GDScript LSP: Can't notify client as none was connected."); +		p_client_id = latest_client_id; +	} +	ERR_FAIL_COND(!clients.has(p_client_id)); +	Ref<LSPeer> peer = clients.get(p_client_id); +	ERR_FAIL_COND(peer == nullptr); + +	Dictionary message = make_request(p_method, p_params, next_server_id); +	next_server_id++; +	String msg = Variant(message).to_json_string(); +	msg = format_output(msg); +	peer->res_queue.push_back(msg.utf8()); +} +  bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const {  	return bool(_EDITOR_GET("network/language_server/enable_smart_resolve"));  } diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index 5a2dd55c46..899446fb42 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -79,6 +79,8 @@ private:  	int latest_client_id = 0;  	int next_client_id = 0; +	int next_server_id = 0; +  	Ref<GDScriptTextDocument> text_document;  	Ref<GDScriptWorkspace> workspace; @@ -107,6 +109,7 @@ public:  	void stop();  	void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1); +	void request_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);  	bool is_smart_resolve_enabled() const;  	bool is_goto_native_symbols_enabled() const; diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 03b1e3fa44..92ce71f395 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -217,8 +217,8 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {  	} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {  		arr = native_member_completions.duplicate(); -		for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.front(); E; E = E->next()) { -			ExtendGDScriptParser *script = E->get(); +		for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) { +			ExtendGDScriptParser *script = E.value;  			const Array &items = script->get_member_completions();  			const int start_size = arr.size(); @@ -428,6 +428,9 @@ GDScriptTextDocument::~GDScriptTextDocument() {  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); +	if (!path.begins_with("res://")) { +		return; +	}  	GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);  	EditorFileSystem::get_singleton()->update_file(path); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 1512b4bb89..371e3de419 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -42,6 +42,7 @@  #include "scene/resources/packed_scene.h"  void GDScriptWorkspace::_bind_methods() { +	ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal);  	ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::did_delete_files);  	ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);  	ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script); @@ -52,6 +53,54 @@ void GDScriptWorkspace::_bind_methods() {  	ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);  } +void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) { +	String function_signature = "func " + function; +	Ref<Script> script = obj->get_script(); + +	String source = script->get_source_code(); + +	if (source.find(function_signature) != -1) { +		return; +	} + +	int first_class = source.find("\nclass "); +	int start_line = 0; +	if (first_class != -1) { +		start_line = source.substr(0, first_class).split("\n").size(); +	} else { +		start_line = source.split("\n").size(); +	} + +	String function_body = "\n\n" + function_signature + "("; +	for (int i = 0; i < args.size(); ++i) { +		function_body += args[i]; +		if (i < args.size() - 1) { +			function_body += ", "; +		} +	} +	function_body += ")"; +	if (EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints")) { +		function_body += " -> void"; +	} +	function_body += ":\n\tpass # Replace with function body.\n"; + +	lsp::TextEdit text_edit; + +	if (first_class != -1) { +		function_body += "\n\n"; +	} +	text_edit.range.end.line = text_edit.range.start.line = start_line; + +	text_edit.newText = function_body; + +	String uri = get_file_uri(script->get_path()); + +	lsp::ApplyWorkspaceEditParams params; +	params.edit.add_edit(uri, text_edit); + +	GDScriptLanguageProtocol::get_singleton()->request_client("workspace/applyEdit", params.to_json()); +} +  void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {  	Array files = p_params["files"];  	for (int i = 0; i < files.size(); ++i) { @@ -212,9 +261,9 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {  	String query = p_params["query"];  	Array arr;  	if (!query.is_empty()) { -		for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) { +		for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {  			Vector<lsp::DocumentedSymbolInformation> script_symbols; -			E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols); +			E.value->get_symbols().symbol_tree_as_list(E.key, script_symbols);  			for (int i = 0; i < script_symbols.size(); ++i) {  				if (query.is_subsequence_ofi(script_symbols[i].name)) {  					lsp::DocumentedSymbolInformation symbol = script_symbols[i]; @@ -233,10 +282,10 @@ Error GDScriptWorkspace::initialize() {  	}  	DocTools *doc = EditorHelp::get_doc_data(); -	for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { -		const DocData::ClassDoc &class_data = E->value(); +	for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) { +		const DocData::ClassDoc &class_data = E.value;  		lsp::DocumentSymbol class_symbol; -		String class_name = E->key(); +		String class_name = E.key;  		class_symbol.name = class_name;  		class_symbol.native_class = class_name;  		class_symbol.kind = lsp::SymbolKind::Class; @@ -344,22 +393,25 @@ Error GDScriptWorkspace::initialize() {  	reload_all_workspace_scripts();  	if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { -		for (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.front(); E; E = E->next()) { +		for (const KeyValue<StringName, lsp::DocumentSymbol> &E : native_symbols) {  			ClassMembers members; -			const lsp::DocumentSymbol &class_symbol = E->get(); +			const lsp::DocumentSymbol &class_symbol = E.value;  			for (int i = 0; i < class_symbol.children.size(); i++) {  				const lsp::DocumentSymbol &symbol = class_symbol.children[i];  				members.set(symbol.name, &symbol);  			} -			native_members.set(E->key(), members); +			native_members.set(E.key, members);  		}  		// cache member completions -		for (Map<String, ExtendGDScriptParser *>::Element *S = scripts.front(); S; S = S->next()) { -			S->get()->get_member_completions(); +		for (const KeyValue<String, ExtendGDScriptParser *> &S : scripts) { +			S.value->get_member_completions();  		}  	} +	EditorNode *editor_node = EditorNode::get_singleton(); +	editor_node->connect("script_add_function_request", callable_mp(this, &GDScriptWorkspace::apply_new_signal)); +  	return OK;  } @@ -551,7 +603,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S  	}  } -const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_requred) { +const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_required) {  	const lsp::DocumentSymbol *symbol = nullptr;  	String path = get_file_path(p_doc_pos.textDocument.uri); @@ -576,7 +628,10 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu  			} else {  				ScriptLanguage::LookupResult ret; -				if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_requred), symbol_identifier, path, nullptr, ret)) { +				if (symbol_identifier == "new" && parser->get_lines()[p_doc_pos.position.line].replace(" ", "").replace("\t", "").find("new(") > -1) { +					symbol_identifier = "_init"; +				} +				if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) {  					if (ret.type == ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION) {  						String target_script_path = path;  						if (!ret.script.is_null()) { @@ -630,8 +685,8 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP  			class_ptr = native_members.next(class_ptr);  		} -		for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) { -			const ExtendGDScriptParser *script = E->get(); +		for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) { +			const ExtendGDScriptParser *script = E.value;  			const ClassMembers &members = script->get_members();  			if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {  				r_list.push_back(*symbol); @@ -731,12 +786,12 @@ GDScriptWorkspace::GDScriptWorkspace() {  GDScriptWorkspace::~GDScriptWorkspace() {  	Set<String> cached_parsers; -	for (Map<String, ExtendGDScriptParser *>::Element *E = parse_results.front(); E; E = E->next()) { -		cached_parsers.insert(E->key()); +	for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) { +		cached_parsers.insert(E.key);  	} -	for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) { -		cached_parsers.insert(E->key()); +	for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) { +		cached_parsers.insert(E.key);  	}  	for (Set<String>::Element *E = cached_parsers.front(); E; E = E->next()) { diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 9496677449..6f5600b5cf 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -62,6 +62,8 @@ protected:  	void list_script_files(const String &p_root_dir, List<String> &r_files); +	void apply_new_signal(Object *obj, String function, PackedStringArray args); +  public:  	String root;  	String root_uri; @@ -85,7 +87,7 @@ public:  	void publish_diagnostics(const String &p_path);  	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_required = 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); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 9ac6c6bd4e..3710a84a28 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -263,19 +263,29 @@ struct WorkspaceEdit {  	 */  	Map<String, Vector<TextEdit>> changes; +	_FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) { +		if (changes.has(uri)) { +			changes[uri].push_back(edit); +		} else { +			Vector<TextEdit> edits; +			edits.push_back(edit); +			changes[uri] = edits; +		} +	} +  	_FORCE_INLINE_ Dictionary to_json() const {  		Dictionary dict;  		Dictionary out_changes; -		for (Map<String, Vector<TextEdit>>::Element *E = changes.front(); E; E = E->next()) { +		for (const KeyValue<String, Vector<TextEdit>> &E : changes) {  			Array edits; -			for (int i = 0; i < E->get().size(); ++i) { +			for (int i = 0; i < E.value.size(); ++i) {  				Dictionary text_edit; -				text_edit["range"] = E->get()[i].range.to_json(); -				text_edit["newText"] = E->get()[i].newText; +				text_edit["range"] = E.value[i].range.to_json(); +				text_edit["newText"] = E.value[i].newText;  				edits.push_back(text_edit);  			} -			out_changes[E->key()] = edits; +			out_changes[E.key] = edits;  		}  		dict["changes"] = out_changes; @@ -1322,6 +1332,18 @@ struct DocumentSymbol {  	}  }; +struct ApplyWorkspaceEditParams { +	WorkspaceEdit edit; + +	Dictionary to_json() { +		Dictionary dict; + +		dict["edit"] = edit.to_json(); + +		return dict; +	} +}; +  struct NativeSymbolInspectParams {  	String native_class;  	String symbol_name; |