diff options
Diffstat (limited to 'modules/gdscript/language_server')
8 files changed, 209 insertions, 33 deletions
| diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index e63b6ab20e..15236d900d 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -49,8 +49,9 @@ void ExtendGDScriptParser::update_diagnostics() {  		diagnostic.code = -1;  		lsp::Range range;  		lsp::Position pos; -		int line = LINE_NUMBER_TO_INDEX(error.line); -		const String &line_text = get_lines()[line]; +		const PackedStringArray lines = get_lines(); +		int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, lines.size() - 1); +		const String &line_text = lines[line];  		pos.line = line;  		pos.character = line_text.length() - line_text.strip_edges(true, false).length();  		range.start = pos; @@ -361,24 +362,73 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN  		r_symbol.detail += " -> " + p_func->get_datatype().to_string();  	} -	for (int i = 0; i < p_func->body->locals.size(); i++) { -		const SuiteNode::Local &local = p_func->body->locals[i]; -		lsp::DocumentSymbol symbol; -		symbol.name = local.name; -		symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable; -		symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line); -		symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column); -		symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line); -		symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column); -		symbol.uri = uri; -		symbol.script_path = path; -		symbol.detail = SuiteNode::Local::CONSTANT ? "const " : "var "; -		symbol.detail += symbol.name; -		if (local.get_datatype().is_hard_type()) { -			symbol.detail += ": " + local.get_datatype().to_string(); +	List<GDScriptParser::SuiteNode *> function_nodes; + +	List<GDScriptParser::Node *> node_stack; +	node_stack.push_back(p_func->body); + +	while (!node_stack.is_empty()) { +		GDScriptParser::Node *node = node_stack[0]; +		node_stack.pop_front(); + +		switch (node->type) { +			case GDScriptParser::TypeNode::IF: { +				GDScriptParser::IfNode *if_node = (GDScriptParser::IfNode *)node; +				node_stack.push_back(if_node->true_block); +				if (if_node->false_block) { +					node_stack.push_back(if_node->false_block); +				} +			} break; + +			case GDScriptParser::TypeNode::FOR: { +				GDScriptParser::ForNode *for_node = (GDScriptParser::ForNode *)node; +				node_stack.push_back(for_node->loop); +			} break; + +			case GDScriptParser::TypeNode::WHILE: { +				GDScriptParser::WhileNode *while_node = (GDScriptParser::WhileNode *)node; +				node_stack.push_back(while_node->loop); +			} break; + +			case GDScriptParser::TypeNode::MATCH_BRANCH: { +				GDScriptParser::MatchBranchNode *match_node = (GDScriptParser::MatchBranchNode *)node; +				node_stack.push_back(match_node->block); +			} break; + +			case GDScriptParser::TypeNode::SUITE: { +				GDScriptParser::SuiteNode *suite_node = (GDScriptParser::SuiteNode *)node; +				function_nodes.push_back(suite_node); +				for (int i = 0; i < suite_node->statements.size(); ++i) { +					node_stack.push_back(suite_node->statements[i]); +				} +			} break; + +			default: +				continue; +		} +	} + +	for (List<GDScriptParser::SuiteNode *>::Element *N = function_nodes.front(); N; N = N->next()) { +		const GDScriptParser::SuiteNode *suite_node = N->get(); +		for (int i = 0; i < suite_node->locals.size(); i++) { +			const SuiteNode::Local &local = suite_node->locals[i]; +			lsp::DocumentSymbol symbol; +			symbol.name = local.name; +			symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable; +			symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line); +			symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column); +			symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line); +			symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column); +			symbol.uri = uri; +			symbol.script_path = path; +			symbol.detail = local.type == SuiteNode::Local::CONSTANT ? "const " : "var "; +			symbol.detail += symbol.name; +			if (local.get_datatype().is_hard_type()) { +				symbol.detail += ": " + local.get_datatype().to_string(); +			} +			symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line)); +			r_symbol.children.push_back(symbol);  		} -		symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line)); -		r_symbol.children.push_back(symbol);  	}  } diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 0432e7caea..c16a7fa889 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -255,7 +255,7 @@ void GDScriptLanguageProtocol::poll() {  	}  } -Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &p_bind_ip) { +Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {  	return server->listen(p_port, p_bind_ip);  } diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index 8b08ae0655..969c38eab6 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -46,7 +46,7 @@ class GDScriptLanguageProtocol : public JSONRPC {  	GDCLASS(GDScriptLanguageProtocol, JSONRPC)  private: -	struct LSPeer : Reference { +	struct LSPeer : RefCounted {  		Ref<StreamPeerTCP> connection;  		uint8_t req_buf[LSP_MAX_BUFFER_SIZE]; @@ -69,7 +69,7 @@ private:  	static GDScriptLanguageProtocol *singleton;  	HashMap<int, Ref<LSPeer>> clients; -	Ref<TCP_Server> server; +	Ref<TCPServer> server;  	int latest_client_id = 0;  	int next_client_id = 0; @@ -97,7 +97,7 @@ public:  	_FORCE_INLINE_ bool is_initialized() const { return _initialized; }  	void poll(); -	Error start(int p_port, const IP_Address &p_bind_ip); +	Error start(int p_port, const IPAddress &p_bind_ip);  	void stop();  	void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1); diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 98ada9de4d..33597c286f 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -30,7 +30,7 @@  #include "gdscript_language_server.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h"  #include "core/os/os.h"  #include "editor/editor_log.h"  #include "editor/editor_node.h" @@ -78,7 +78,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {  void GDScriptLanguageServer::start() {  	port = (int)_EDITOR_GET("network/language_server/remote_port");  	use_thread = (bool)_EDITOR_GET("network/language_server/use_thread"); -	if (protocol.start(port, IP_Address("127.0.0.1")) == OK) { +	if (protocol.start(port, IPAddress("127.0.0.1")) == OK) {  		EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);  		if (use_thread) {  			thread_running = true; diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index 792e601bc1..17f1d5d5e3 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -31,12 +31,12 @@  #ifndef GDSCRIPT_TEXT_DOCUMENT_H  #define GDSCRIPT_TEXT_DOCUMENT_H -#include "core/object/reference.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" +#include "core/object/ref_counted.h"  #include "lsp.hpp" -class GDScriptTextDocument : public Reference { -	GDCLASS(GDScriptTextDocument, Reference) +class GDScriptTextDocument : public RefCounted { +	GDCLASS(GDScriptTextDocument, RefCounted)  protected:  	static void _bind_methods(); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 69cad1a335..9b7b2b36b4 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("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);  	ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script); @@ -51,6 +52,16 @@ void GDScriptWorkspace::_bind_methods() {  	ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);  } +void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) { +	Array files = p_params["files"]; +	for (int i = 0; i < files.size(); ++i) { +		Dictionary file = files[i]; +		String uri = file["uri"]; +		String path = get_file_path(uri); +		parse_script(path, ""); +	} +} +  void GDScriptWorkspace::remove_cache_parser(const String &p_path) {  	Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path);  	Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path); diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 7fd8bfcf20..8b166a873c 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -37,8 +37,8 @@  #include "gdscript_extend_parser.h"  #include "lsp.hpp" -class GDScriptWorkspace : public Reference { -	GDCLASS(GDScriptWorkspace, Reference); +class GDScriptWorkspace : public RefCounted { +	GDCLASS(GDScriptWorkspace, RefCounted);  private:  	void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners); @@ -89,6 +89,7 @@ public:  	void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);  	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);  	GDScriptWorkspace();  	~GDScriptWorkspace(); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 6635098be2..a7dcfdb22d 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -766,7 +766,7 @@ 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. +// And here C++ compilers are unhappy with our enumeration name like Color, File, RefCounted etc.  /**   * The kind of a completion entry.   */ @@ -788,7 +788,7 @@ static const int Keyword = 14;  static const int Snippet = 15;  static const int Color = 16;  static const int File = 17; -static const int Reference = 18; +static const int RefCounted = 18;  static const int Folder = 19;  static const int EnumMember = 20;  static const int Constant = 21; @@ -1528,6 +1528,114 @@ struct SignatureHelp {  	}  }; +/** + * A pattern to describe in which file operation requests or notifications + * the server is interested in. + */ +struct FileOperationPattern { +	/** +	 * The glob pattern to match. +	 */ +	String glob = "**/*.gd"; + +	/** +	 * Whether to match `file`s or `folder`s with this pattern. +	 * +	 * Matches both if undefined. +	 */ +	String matches = "file"; + +	Dictionary to_json() const { +		Dictionary dict; + +		dict["glob"] = glob; +		dict["matches"] = matches; + +		return dict; +	} +}; + +/** + * A filter to describe in which file operation requests or notifications + * the server is interested in. + */ +struct FileOperationFilter { +	/** +	 * The actual file operation pattern. +	 */ +	FileOperationPattern pattern; + +	Dictionary to_json() const { +		Dictionary dict; + +		dict["pattern"] = pattern.to_json(); + +		return dict; +	} +}; + +/** + * The options to register for file operations. + */ +struct FileOperationRegistrationOptions { +	/** +	 * The actual filters. +	 */ +	Vector<FileOperationFilter> filters; + +	FileOperationRegistrationOptions() { +		filters.push_back(FileOperationFilter()); +	} + +	Dictionary to_json() const { +		Dictionary dict; + +		Array filts; +		for (int i = 0; i < filters.size(); i++) { +			filts.push_back(filters[i].to_json()); +		} +		dict["filters"] = filts; + +		return dict; +	} +}; + +/** + * The server is interested in file notifications/requests. + */ +struct FileOperations { +	/** +	 * The server is interested in receiving didDeleteFiles file notifications. +	 */ +	FileOperationRegistrationOptions didDelete; + +	Dictionary to_json() const { +		Dictionary dict; + +		dict["didDelete"] = didDelete.to_json(); + +		return dict; +	} +}; + +/** + * Workspace specific server capabilities + */ +struct Workspace { +	/** +	 * The server is interested in file notifications/requests. +	 */ +	FileOperations fileOperations; + +	Dictionary to_json() const { +		Dictionary dict; + +		dict["fileOperations"] = fileOperations.to_json(); + +		return dict; +	} +}; +  struct ServerCapabilities {  	/**  	 * Defines how text documents are synced. Is either a detailed structure defining each notification or @@ -1590,6 +1698,11 @@ struct ServerCapabilities {  	bool workspaceSymbolProvider = true;  	/** +	 * The server supports workspace folder. +	 */ +	Workspace workspace; + +	/**  	 * The server provides code actions. The `CodeActionOptions` return type is only  	 * valid if the client signals code action literal support via the property  	 * `textDocument.codeAction.codeActionLiteralSupport`. @@ -1676,6 +1789,7 @@ struct ServerCapabilities {  		dict["documentHighlightProvider"] = documentHighlightProvider;  		dict["documentSymbolProvider"] = documentSymbolProvider;  		dict["workspaceSymbolProvider"] = workspaceSymbolProvider; +		dict["workspace"] = workspace.to_json();  		dict["codeActionProvider"] = codeActionProvider;  		dict["documentFormattingProvider"] = documentFormattingProvider;  		dict["documentRangeFormattingProvider"] = documentRangeFormattingProvider; |