diff options
Diffstat (limited to 'modules/gdscript/language_server')
5 files changed, 209 insertions, 9 deletions
| diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 69ddbe5d1e..c0013ac23a 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -42,10 +42,12 @@ void GDScriptTextDocument::_bind_methods() {  	ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);  	ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose);  	ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange); +	ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave);  	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); +	ClassDB::bind_method(D_METHOD("rename"), &GDScriptTextDocument::rename);  	ClassDB::bind_method(D_METHOD("foldingRange"), &GDScriptTextDocument::foldingRange);  	ClassDB::bind_method(D_METHOD("codeLens"), &GDScriptTextDocument::codeLens);  	ClassDB::bind_method(D_METHOD("documentLink"), &GDScriptTextDocument::documentLink); @@ -79,6 +81,20 @@ void GDScriptTextDocument::didChange(const Variant &p_param) {  	sync_script_content(doc.uri, doc.text);  } +void GDScriptTextDocument::didSave(const Variant &p_param) { +	lsp::TextDocumentItem doc = load_document_item(p_param); +	Dictionary dict = p_param; +	String text = dict["text"]; + +	sync_script_content(doc.uri, text); + +	/*String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri); + +	Ref<GDScript> script = ResourceLoader::load(path); +	script->load_source_code(path); +	script->reload(true);*/ +} +  lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {  	lsp::TextDocumentItem doc;  	Dictionary params = p_param; @@ -215,6 +231,14 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {  	return arr;  } +Dictionary GDScriptTextDocument::rename(const Dictionary &p_params) { +	lsp::TextDocumentPositionParams params; +	params.load(p_params); +	String new_name = p_params["newName"]; + +	return GDScriptLanguageProtocol::get_singleton()->get_workspace()->rename(params, new_name); +} +  Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {  	lsp::CompletionItem item;  	item.load(p_params); @@ -405,7 +429,11 @@ 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);  	GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); +  	EditorFileSystem::get_singleton()->update_file(path); +	Ref<GDScript> script = ResourceLoader::load(path); +	script->load_source_code(path); +	script->reload(true);  }  void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) { diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index e2987f779c..9021c84a3f 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -45,6 +45,7 @@ protected:  	void didOpen(const Variant &p_param);  	void didClose(const Variant &p_param);  	void didChange(const Variant &p_param); +	void didSave(const Variant &p_param);  	void sync_script_content(const String &p_path, const String &p_content);  	void show_native_symbol_in_editor(const String &p_symbol_id); @@ -61,6 +62,7 @@ public:  	Array documentSymbol(const Dictionary &p_params);  	Array completion(const Dictionary &p_params);  	Dictionary resolve(const Dictionary &p_params); +	Dictionary rename(const Dictionary &p_params);  	Array foldingRange(const Dictionary &p_params);  	Array codeLens(const Dictionary &p_params);  	Array documentLink(const Dictionary &p_params); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index e6c819b22f..4fc229a0f8 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -116,6 +116,36 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_  	return nullptr;  } +const lsp::DocumentSymbol *GDScriptWorkspace::get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier) { +	for (int i = 0; i < p_parent->children.size(); ++i) { +		const lsp::DocumentSymbol *parameter_symbol = &p_parent->children[i]; +		if (!parameter_symbol->detail.is_empty() && parameter_symbol->name == symbol_identifier) { +			return parameter_symbol; +		} +	} + +	return nullptr; +} + +const lsp::DocumentSymbol *GDScriptWorkspace::get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier) { +	const lsp::DocumentSymbol *class_symbol = &p_parser->get_symbols(); + +	for (int i = 0; i < class_symbol->children.size(); ++i) { +		if (class_symbol->children[i].kind == lsp::SymbolKind::Function || class_symbol->children[i].kind == lsp::SymbolKind::Class) { +			const lsp::DocumentSymbol *function_symbol = &class_symbol->children[i]; + +			for (int l = 0; l < function_symbol->children.size(); ++l) { +				const lsp::DocumentSymbol *local = &function_symbol->children[l]; +				if (!local->detail.is_empty() && local->name == p_symbol_identifier) { +					return local; +				} +			} +		} +	} + +	return nullptr; +} +  void GDScriptWorkspace::reload_all_workspace_scripts() {  	List<String> paths;  	list_script_files("res://", paths); @@ -231,18 +261,13 @@ Error GDScriptWorkspace::initialize() {  			class_symbol.children.push_back(symbol);  		} -		Vector<DocData::PropertyDoc> properties; -		properties.append_array(class_data.properties); -		const int theme_prop_start_idx = properties.size(); -		properties.append_array(class_data.theme_properties); -  		for (int i = 0; i < class_data.properties.size(); i++) {  			const DocData::PropertyDoc &data = class_data.properties[i];  			lsp::DocumentSymbol symbol;  			symbol.name = data.name;  			symbol.native_class = class_name;  			symbol.kind = lsp::SymbolKind::Property; -			symbol.detail = String(i >= theme_prop_start_idx ? "<Theme> var" : "var") + " " + class_name + "." + data.name; +			symbol.detail = "var " + class_name + "." + data.name;  			if (data.enumeration.length()) {  				symbol.detail += ": " + data.enumeration;  			} else { @@ -252,6 +277,17 @@ Error GDScriptWorkspace::initialize() {  			class_symbol.children.push_back(symbol);  		} +		for (int i = 0; i < class_data.theme_properties.size(); i++) { +			const DocData::ThemeItemDoc &data = class_data.theme_properties[i]; +			lsp::DocumentSymbol symbol; +			symbol.name = data.name; +			symbol.native_class = class_name; +			symbol.kind = lsp::SymbolKind::Property; +			symbol.detail = "<Theme> var " + class_name + "." + data.name + ": " + data.type; +			symbol.documentation = data.description; +			class_symbol.children.push_back(symbol); +		} +  		Vector<DocData::MethodDoc> methods_signals;  		methods_signals.append_array(class_data.methods);  		const int signal_start_idx = methods_signals.size(); @@ -350,6 +386,50 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont  	return err;  } +Dictionary GDScriptWorkspace::rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name) { +	Error err; +	String path = get_file_path(p_doc_pos.textDocument.uri); + +	lsp::WorkspaceEdit edit; + +	List<String> paths; +	list_script_files("res://", paths); + +	const lsp::DocumentSymbol *reference_symbol = resolve_symbol(p_doc_pos); +	if (reference_symbol) { +		String identifier = reference_symbol->name; + +		for (List<String>::Element *PE = paths.front(); PE; PE = PE->next()) { +			PackedStringArray content = FileAccess::get_file_as_string(PE->get(), &err).split("\n"); +			for (int i = 0; i < content.size(); ++i) { +				String line = content[i]; + +				int character = line.find(identifier); +				while (character > -1) { +					lsp::TextDocumentPositionParams params; + +					lsp::TextDocumentIdentifier text_doc; +					text_doc.uri = get_file_uri(PE->get()); + +					params.textDocument = text_doc; +					params.position.line = i; +					params.position.character = character; + +					const lsp::DocumentSymbol *other_symbol = resolve_symbol(params); + +					if (other_symbol == reference_symbol) { +						edit.add_change(text_doc.uri, i, character, character + identifier.length(), new_name); +					} + +					character = line.find(identifier, character + 1); +				} +			} +		} +	} + +	return edit.to_json(); +} +  Error GDScriptWorkspace::parse_local_script(const String &p_path) {  	Error err;  	String content = FileAccess::get_file_as_string(p_path, &err); @@ -440,8 +520,29 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S  	if (const ExtendGDScriptParser *parser = get_parse_result(path)) {  		Node *owner_scene_node = _get_owner_scene_node(path); + +		Array stack; +		Node *current = nullptr; +		stack.push_back(owner_scene_node); + +		while (!stack.is_empty()) { +			current = stack.pop_back(); +			Ref<GDScript> script = current->get_script(); +			if (script.is_valid() && script->get_path() == path) { +				break; +			} +			for (int i = 0; i < current->get_child_count(); ++i) { +				stack.push_back(current->get_child(i)); +			} +		} + +		Ref<GDScript> script = current->get_script(); +		if (!script.is_valid() || script->get_path() != path) { +			current = owner_scene_node; +		} +  		String code = parser->get_text_for_completion(p_params.position); -		GDScriptLanguage::get_singleton()->complete_code(code, path, owner_scene_node, r_options, forced, call_hint); +		GDScriptLanguage::get_singleton()->complete_code(code, path, current, r_options, forced, call_hint);  		if (owner_scene_node) {  			memdelete(owner_scene_node);  		} @@ -478,10 +579,16 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu  						String target_script_path = path;  						if (!ret.script.is_null()) {  							target_script_path = ret.script->get_path(); +						} else if (!ret.class_path.is_empty()) { +							target_script_path = ret.class_path;  						}  						if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) {  							symbol = target_parser->get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(ret.location)); + +							if (symbol && symbol->kind == lsp::SymbolKind::Function && symbol->name != symbol_identifier) { +								symbol = get_parameter_symbol(symbol, symbol_identifier); +							}  						}  					} else { @@ -493,6 +600,10 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu  					}  				} else {  					symbol = parser->get_member_symbol(symbol_identifier); + +					if (!symbol) { +						symbol = get_local_symbol(parser, symbol_identifier); +					}  				}  			}  		} diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 8b166a873c..9496677449 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -52,6 +52,8 @@ protected:  	const lsp::DocumentSymbol *get_native_symbol(const String &p_class, const String &p_member = "") const;  	const lsp::DocumentSymbol *get_script_symbol(const String &p_path) const; +	const lsp::DocumentSymbol *get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier); +	const lsp::DocumentSymbol *get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier);  	void reload_all_workspace_scripts(); @@ -90,6 +92,7 @@ public:  	Dictionary generate_script_api(const String &p_path);  	Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);  	void did_delete_files(const Dictionary &p_params); +	Dictionary rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name);  	GDScriptWorkspace();  	~GDScriptWorkspace(); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 0138f132ad..9ac6c6bd4e 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -255,6 +255,62 @@ struct TextEdit {  };  /** + * The edits to be applied. + */ +struct WorkspaceEdit { +	/** +	 * Holds changes to existing resources. +	 */ +	Map<String, Vector<TextEdit>> changes; + +	_FORCE_INLINE_ Dictionary to_json() const { +		Dictionary dict; + +		Dictionary out_changes; +		for (Map<String, Vector<TextEdit>>::Element *E = changes.front(); E; E = E->next()) { +			Array edits; +			for (int i = 0; i < E->get().size(); ++i) { +				Dictionary text_edit; +				text_edit["range"] = E->get()[i].range.to_json(); +				text_edit["newText"] = E->get()[i].newText; +				edits.push_back(text_edit); +			} +			out_changes[E->key()] = edits; +		} +		dict["changes"] = out_changes; + +		return dict; +	} + +	_FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) { +		if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) { +			Vector<TextEdit> edit_list = E->value(); +			for (int i = 0; i < edit_list.size(); ++i) { +				TextEdit edit = edit_list[i]; +				if (edit.range.start.character == start_character) { +					return; +				} +			} +		} + +		TextEdit new_edit; +		new_edit.newText = new_text; +		new_edit.range.start.line = line; +		new_edit.range.start.character = start_character; +		new_edit.range.end.line = line; +		new_edit.range.end.character = end_character; + +		if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) { +			E->value().push_back(new_edit); +		} else { +			Vector<TextEdit> edit_list; +			edit_list.push_back(new_edit); +			changes.insert(uri, edit_list); +		} +	} +}; + +/**   * Represents a reference to a command.   * Provides a title which will be used to represent a command in the UI.   * Commands are identified by a string identifier. @@ -486,7 +542,7 @@ struct TextDocumentSyncOptions {  	 * If present save notifications are sent to the server. If omitted the notification should not be  	 * sent.  	 */ -	bool save = false; +	SaveOptions save;  	Dictionary to_json() {  		Dictionary dict; @@ -494,7 +550,7 @@ struct TextDocumentSyncOptions {  		dict["willSave"] = willSave;  		dict["openClose"] = openClose;  		dict["change"] = change; -		dict["save"] = save; +		dict["save"] = save.to_json();  		return dict;  	}  }; |