浏览代码

Merge pull request #32544 from GodotExplorer/gdscript-lsp-show_native_symbol

Improvements for gdscript-lsp server
Rémi Verschelde 6 年之前
父节点
当前提交
872af14e6f

+ 6 - 64
modules/gdscript/language_server/gdscript_extend_parser.cpp

@@ -157,7 +157,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 	r_symbol.selectionRange.start.line = r_symbol.range.start.line;
 	r_symbol.detail = "class " + r_symbol.name;
 	bool is_root_class = &r_symbol == &class_symbol;
-	r_symbol.documentation = parse_documentation_as_markdown(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class);
+	r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class);
 
 	for (int i = 0; i < p_class->variables.size(); ++i) {
 
@@ -184,7 +184,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 			symbol.detail += " = " + JSON::print(m.default_value);
 		}
 
-		symbol.documentation = parse_documentation_as_markdown(line);
+		symbol.documentation = parse_documentation(line);
 		symbol.uri = uri;
 		symbol.script_path = path;
 
@@ -204,7 +204,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 		symbol.range.end.line = symbol.range.start.line;
 		symbol.range.end.character = lines[line].length();
 		symbol.selectionRange.start.line = symbol.range.start.line;
-		symbol.documentation = parse_documentation_as_markdown(line);
+		symbol.documentation = parse_documentation(line);
 		symbol.uri = uri;
 		symbol.script_path = path;
 		symbol.detail = "signal " + signal.name + "(";
@@ -233,7 +233,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 		symbol.range.end.line = symbol.range.start.line;
 		symbol.range.end.character = lines[line].length();
 		symbol.selectionRange.start.line = symbol.range.start.line;
-		symbol.documentation = parse_documentation_as_markdown(line);
+		symbol.documentation = parse_documentation(line);
 		symbol.uri = uri;
 		symbol.script_path = path;
 
@@ -301,7 +301,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
 	r_symbol.range.end.line = MAX(p_func->body->end_line - 2, p_func->body->line);
 	r_symbol.range.end.character = lines[r_symbol.range.end.line].length();
 	r_symbol.selectionRange.start.line = r_symbol.range.start.line;
-	r_symbol.documentation = parse_documentation_as_markdown(line);
+	r_symbol.documentation = parse_documentation(line);
 	r_symbol.uri = uri;
 	r_symbol.script_path = path;
 
@@ -359,65 +359,11 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
 		if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) {
 			symbol.detail += ": " + var->datatype.to_string();
 		}
-		symbol.documentation = parse_documentation_as_markdown(line);
+		symbol.documentation = parse_documentation(line);
 		r_symbol.children.push_back(symbol);
 	}
 }
 
-String ExtendGDScriptParser::marked_documentation(const String &p_bbcode) {
-
-	String markdown = p_bbcode.strip_edges();
-
-	Vector<String> lines = markdown.split("\n");
-	bool in_code_block = false;
-	int code_block_indent = -1;
-
-	markdown = "";
-	for (int i = 0; i < lines.size(); i++) {
-		String line = lines[i];
-		int block_start = line.find("[codeblock]");
-		if (block_start != -1) {
-			code_block_indent = block_start;
-			in_code_block = true;
-			line = "\n";
-		} else if (in_code_block) {
-			line = "\t" + line.substr(code_block_indent, line.length());
-		}
-
-		if (in_code_block && line.find("[/codeblock]") != -1) {
-			line = "\n";
-			in_code_block = false;
-		}
-
-		if (!in_code_block) {
-			line = line.strip_edges();
-			line = line.replace("[code]", "`");
-			line = line.replace("[/code]", "`");
-			line = line.replace("[i]", "*");
-			line = line.replace("[/i]", "*");
-			line = line.replace("[b]", "**");
-			line = line.replace("[/b]", "**");
-			line = line.replace("[u]", "__");
-			line = line.replace("[/u]", "__");
-			line = line.replace("[method ", "`");
-			line = line.replace("[member ", "`");
-			line = line.replace("[signal ", "`");
-			line = line.replace("[enum ", "`");
-			line = line.replace("[constant ", "`");
-			line = line.replace("[", "`");
-			line = line.replace("]", "`");
-		}
-
-		if (!in_code_block && i < lines.size() - 1) {
-			line += "\n\n";
-		} else if (i < lines.size() - 1) {
-			line += "\n";
-		}
-		markdown += line;
-	}
-	return markdown;
-}
-
 String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
 	ERR_FAIL_INDEX_V(p_line, lines.size(), String());
 
@@ -576,10 +522,6 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i
 	return ret;
 }
 
-String ExtendGDScriptParser::parse_documentation_as_markdown(int p_line, bool p_docs_down) {
-	return marked_documentation(parse_documentation(p_line, p_docs_down));
-}
-
 const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const {
 	if (p_line <= 0) {
 		return &class_symbol;

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

@@ -75,11 +75,6 @@ class ExtendGDScriptParser : public GDScriptParser {
 
 	Array member_completions;
 
-	String parse_documentation_as_markdown(int p_line, bool p_docs_down = false);
-
-public:
-	static String marked_documentation(const String &p_bbcode);
-
 public:
 	_FORCE_INLINE_ const String &get_path() const { return path; }
 	_FORCE_INLINE_ const Vector<String> &get_lines() const { return lines; }

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

@@ -153,7 +153,13 @@ Error GDScriptLanguageProtocol::start(int p_port) {
 }
 
 void GDScriptLanguageProtocol::stop() {
+	const int *ptr = clients.next(NULL);
+	while (ptr) {
+		clients.get(*ptr)->close();
+		ptr = clients.next(ptr);
+	}
 	server->stop();
+	clients.clear();
 }
 
 void GDScriptLanguageProtocol::notify_all_clients(const String &p_method, const Variant &p_params) {

+ 49 - 24
modules/gdscript/language_server/gdscript_text_document.cpp

@@ -39,6 +39,7 @@
 void GDScriptTextDocument::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
 	ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
+	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);
@@ -75,6 +76,11 @@ lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_
 	return doc;
 }
 
+void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol *symbol) {
+	ERR_FAIL_NULL(symbol);
+	GDScriptLanguageProtocol::get_singleton()->notify_client("gdscript/show_native_symbol", symbol->to_json(true));
+}
+
 void GDScriptTextDocument::initialize() {
 
 	if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
@@ -102,6 +108,21 @@ void GDScriptTextDocument::initialize() {
 	}
 }
 
+Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) {
+
+	Variant ret;
+
+	lsp::NativeSymbolInspectParams params;
+	params.load(p_params);
+
+	if (const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_native_symbol(params)) {
+		ret = symbol->to_json(true);
+		notify_client_show_symbol(symbol);
+	}
+
+	return ret;
+}
+
 Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
 	Dictionary params = p_params["textDocument"];
 	String uri = params["uri"];
@@ -335,31 +356,35 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) {
 		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());
-		} else if (!symbol->native_class.empty() && GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
-			String id;
-			switch (symbol->kind) {
-				case lsp::SymbolKind::Class:
-					id = "class_name:" + symbol->name;
-					break;
-				case lsp::SymbolKind::Constant:
-					id = "class_constant:" + symbol->native_class + ":" + symbol->name;
-					break;
-				case lsp::SymbolKind::Property:
-				case lsp::SymbolKind::Variable:
-					id = "class_property:" + symbol->native_class + ":" + symbol->name;
-					break;
-				case lsp::SymbolKind::Enum:
-					id = "class_enum:" + symbol->native_class + ":" + symbol->name;
-					break;
-				case lsp::SymbolKind::Method:
-				case lsp::SymbolKind::Function:
-					id = "class_method:" + symbol->native_class + ":" + symbol->name;
-					break;
-				default:
-					id = "class_global:" + symbol->native_class + ":" + symbol->name;
-					break;
+		} else if (!symbol->native_class.empty()) {
+			if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
+				String id;
+				switch (symbol->kind) {
+					case lsp::SymbolKind::Class:
+						id = "class_name:" + symbol->name;
+						break;
+					case lsp::SymbolKind::Constant:
+						id = "class_constant:" + symbol->native_class + ":" + symbol->name;
+						break;
+					case lsp::SymbolKind::Property:
+					case lsp::SymbolKind::Variable:
+						id = "class_property:" + symbol->native_class + ":" + symbol->name;
+						break;
+					case lsp::SymbolKind::Enum:
+						id = "class_enum:" + symbol->native_class + ":" + symbol->name;
+						break;
+					case lsp::SymbolKind::Method:
+					case lsp::SymbolKind::Function:
+						id = "class_method:" + symbol->native_class + ":" + symbol->name;
+						break;
+					default:
+						id = "class_global:" + symbol->native_class + ":" + symbol->name;
+						break;
+				}
+				call_deferred("show_native_symbol_in_editor", id);
+			} else {
+				notify_client_show_symbol(symbol);
 			}
-			call_deferred("show_native_symbol_in_editor", id);
 		}
 	} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
 

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

@@ -52,8 +52,10 @@ protected:
 
 private:
 	lsp::TextDocumentItem load_document_item(const Variant &p_param);
+	void notify_client_show_symbol(const lsp::DocumentSymbol *symbol);
 
 public:
+	Variant nativeSymbol(const Dictionary &p_params);
 	Array documentSymbol(const Dictionary &p_params);
 	Array completion(const Dictionary &p_params);
 	Dictionary resolve(const Dictionary &p_params);

+ 22 - 4
modules/gdscript/language_server/gdscript_workspace.cpp

@@ -198,7 +198,7 @@ Error GDScriptWorkspace::initialize() {
 		if (!class_data.inherits.empty()) {
 			class_symbol.detail += " extends " + class_data.inherits;
 		}
-		class_symbol.documentation = ExtendGDScriptParser::marked_documentation(class_data.brief_description) + "\n" + ExtendGDScriptParser::marked_documentation(class_data.description);
+		class_symbol.documentation = class_data.brief_description + "\n" + class_data.description;
 
 		for (int i = 0; i < class_data.constants.size(); i++) {
 			const DocData::ConstantDoc &const_data = class_data.constants[i];
@@ -211,7 +211,7 @@ Error GDScriptWorkspace::initialize() {
 				symbol.detail += ": " + const_data.enumeration;
 			}
 			symbol.detail += " = " + const_data.value;
-			symbol.documentation = ExtendGDScriptParser::marked_documentation(const_data.description);
+			symbol.documentation = const_data.description;
 			class_symbol.children.push_back(symbol);
 		}
 
@@ -232,7 +232,7 @@ Error GDScriptWorkspace::initialize() {
 			} else {
 				symbol.detail += ": " + data.type;
 			}
-			symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description);
+			symbol.documentation = data.description;
 			class_symbol.children.push_back(symbol);
 		}
 
@@ -270,7 +270,7 @@ Error GDScriptWorkspace::initialize() {
 			}
 
 			symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + data.return_type;
-			symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description);
+			symbol.documentation = data.description;
 			class_symbol.children.push_back(symbol);
 		}
 
@@ -475,6 +475,24 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
 	}
 }
 
+const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {
+
+	if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {
+		const lsp::DocumentSymbol &symbol = E->get();
+		if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) {
+			return &symbol;
+		}
+
+		for (int i = 0; i < symbol.children.size(); ++i) {
+			if (symbol.children[i].name == p_params.symbol_name) {
+				return &(symbol.children[i]);
+			}
+		}
+	}
+
+	return NULL;
+}
+
 void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) {
 	if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) {
 		const List<lsp::DocumentLink> &links = parser->get_document_links();

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

@@ -80,7 +80,7 @@ public:
 
 	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);
-
+	const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params);
 	void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
 	Dictionary generate_script_api(const String &p_path);
 

+ 115 - 29
modules/gdscript/language_server/lsp.hpp

@@ -37,6 +37,9 @@ namespace lsp {
 
 typedef String DocumentUri;
 
+/** Format BBCode documentation from DocData to markdown */
+static String marked_documentation(const String &p_bbcode);
+
 /**
  * Text documents are identified using a URI. On the protocol level, URIs are passed as strings.
  */
@@ -282,6 +285,9 @@ struct Command {
 	}
 };
 
+// Use namespace instead of enumeration to follow the LSP specifications
+// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
+
 namespace TextDocumentSyncKind {
 /**
 	 * Documents should not be synced at all.
@@ -592,6 +598,7 @@ struct TextDocumentContentChangeEvent {
 	}
 };
 
+// Use namespace instead of enumeration to follow the LSP specifications
 namespace DiagnosticSeverity {
 /**
 	 * Reports an error.
@@ -692,6 +699,7 @@ struct Diagnostic {
 	}
 };
 
+// Use namespace instead of enumeration to follow the LSP specifications
 /**
  * Describes the content type that a client supports in various
  * result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
@@ -756,6 +764,9 @@ 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.
 /**
  * The kind of a completion entry.
  */
@@ -787,6 +798,7 @@ static const int Operator = 24;
 static const int TypeParameter = 25;
 }; // namespace CompletionItemKind
 
+// Use namespace instead of enumeration to follow the LSP specifications
 /**
  * Defines whether the insert text in a completion item should be interpreted as
  * plain text or a snippet.
@@ -979,36 +991,39 @@ struct CompletionList {
 	Vector<CompletionItem> items;
 };
 
+// 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 String, Array, Object etc
 /**
  * A symbol kind.
  */
 namespace SymbolKind {
-static const int File = 1;
-static const int Module = 2;
-static const int Namespace = 3;
-static const int Package = 4;
-static const int Class = 5;
-static const int Method = 6;
-static const int Property = 7;
-static const int Field = 8;
-static const int Constructor = 9;
-static const int Enum = 10;
-static const int Interface = 11;
-static const int Function = 12;
-static const int Variable = 13;
-static const int Constant = 14;
-static const int String = 15;
-static const int Number = 16;
-static const int Boolean = 17;
-static const int Array = 18;
-static const int Object = 19;
-static const int Key = 20;
-static const int Null = 21;
-static const int EnumMember = 22;
-static const int Struct = 23;
-static const int Event = 24;
-static const int Operator = 25;
-static const int TypeParameter = 26;
+static const int File = 0;
+static const int Module = 1;
+static const int Namespace = 2;
+static const int Package = 3;
+static const int Class = 4;
+static const int Method = 5;
+static const int Property = 6;
+static const int Field = 7;
+static const int Constructor = 8;
+static const int Enum = 9;
+static const int Interface = 10;
+static const int Function = 11;
+static const int Variable = 12;
+static const int Constant = 13;
+static const int String = 14;
+static const int Number = 15;
+static const int Boolean = 16;
+static const int Array = 17;
+static const int Object = 18;
+static const int Key = 19;
+static const int Null = 20;
+static const int EnumMember = 21;
+static const int Struct = 22;
+static const int Event = 23;
+static const int Operator = 24;
+static const int TypeParameter = 25;
 }; // namespace SymbolKind
 
 /**
@@ -1134,7 +1149,7 @@ struct DocumentSymbol {
 	 */
 	Vector<DocumentSymbol> children;
 
-	_FORCE_INLINE_ Dictionary to_json() const {
+	Dictionary to_json(bool with_doc = false) const {
 		Dictionary dict;
 		dict["name"] = name;
 		dict["detail"] = detail;
@@ -1142,10 +1157,14 @@ struct DocumentSymbol {
 		dict["deprecated"] = deprecated;
 		dict["range"] = range.to_json();
 		dict["selectionRange"] = selectionRange.to_json();
+		if (with_doc) {
+			dict["documentation"] = documentation;
+			dict["native_class"] = native_class;
+		}
 		Array arr;
 		arr.resize(children.size());
 		for (int i = 0; i < children.size(); i++) {
-			arr[i] = children[i].to_json();
+			arr[i] = children[i].to_json(with_doc);
 		}
 		dict["children"] = arr;
 		return dict;
@@ -1177,7 +1196,7 @@ struct DocumentSymbol {
 			markdown.value = "\t" + detail + "\n\n";
 		}
 		if (documentation.length()) {
-			markdown.value += documentation + "\n\n";
+			markdown.value += marked_documentation(documentation) + "\n\n";
 		}
 		if (script_path.length()) {
 			markdown.value += "Defined in [" + script_path + "](" + uri + ")";
@@ -1229,6 +1248,17 @@ struct DocumentSymbol {
 	}
 };
 
+struct NativeSymbolInspectParams {
+
+	String native_class;
+	String symbol_name;
+
+	void load(const Dictionary &p_params) {
+		native_class = p_params["native_class"];
+		symbol_name = p_params["symbol_name"];
+	}
+};
+
 /**
  * Enum of known range kinds
  */
@@ -1289,6 +1319,7 @@ struct FoldingRange {
 	}
 };
 
+// Use namespace instead of enumeration to follow the LSP specifications
 /**
  * How a completion was triggered
  */
@@ -1536,6 +1567,61 @@ struct InitializeResult {
 	}
 };
 
+/** Format BBCode documentation from DocData to markdown */
+static String marked_documentation(const String &p_bbcode) {
+
+	String markdown = p_bbcode.strip_edges();
+
+	Vector<String> lines = markdown.split("\n");
+	bool in_code_block = false;
+	int code_block_indent = -1;
+
+	markdown = "";
+	for (int i = 0; i < lines.size(); i++) {
+		String line = lines[i];
+		int block_start = line.find("[codeblock]");
+		if (block_start != -1) {
+			code_block_indent = block_start;
+			in_code_block = true;
+			line = "\n";
+		} else if (in_code_block) {
+			line = "\t" + line.substr(code_block_indent, line.length());
+		}
+
+		if (in_code_block && line.find("[/codeblock]") != -1) {
+			line = "\n";
+			in_code_block = false;
+		}
+
+		if (!in_code_block) {
+			line = line.strip_edges();
+			line = line.replace("[code]", "`");
+			line = line.replace("[/code]", "`");
+			line = line.replace("[i]", "*");
+			line = line.replace("[/i]", "*");
+			line = line.replace("[b]", "**");
+			line = line.replace("[/b]", "**");
+			line = line.replace("[u]", "__");
+			line = line.replace("[/u]", "__");
+			line = line.replace("[method ", "`");
+			line = line.replace("[member ", "`");
+			line = line.replace("[signal ", "`");
+			line = line.replace("[enum ", "`");
+			line = line.replace("[constant ", "`");
+			line = line.replace("[", "`");
+			line = line.replace("]", "`");
+		}
+
+		if (!in_code_block && i < lines.size() - 1) {
+			line += "\n\n";
+		} else if (i < lines.size() - 1) {
+			line += "\n";
+		}
+		markdown += line;
+	}
+	return markdown;
+}
+
 } // namespace lsp
 
 #endif