Merge pull request #34920 from GodotExplorer/lsp-signatureHelper
GDScript LSP: Implement signatureHelp
This commit is contained in:
commit
5a8107f70a
8 changed files with 232 additions and 1 deletions
|
@ -522,6 +522,51 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i
|
|||
return ret;
|
||||
}
|
||||
|
||||
Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_position.line, lines.size(), ERR_INVALID_PARAMETER);
|
||||
|
||||
int bracket_stack = 0;
|
||||
int index = 0;
|
||||
|
||||
bool found = false;
|
||||
for (int l = p_position.line; l >= 0; --l) {
|
||||
String line = lines[l];
|
||||
int c = line.length() - 1;
|
||||
if (l == p_position.line) {
|
||||
c = MIN(c, p_position.character - 1);
|
||||
}
|
||||
|
||||
while (c >= 0) {
|
||||
const CharType &charactor = line[c];
|
||||
if (charactor == ')') {
|
||||
++bracket_stack;
|
||||
} else if (charactor == '(') {
|
||||
--bracket_stack;
|
||||
if (bracket_stack < 0) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (bracket_stack <= 0 && charactor == ',') {
|
||||
++index;
|
||||
}
|
||||
--c;
|
||||
if (found) {
|
||||
r_func_pos.character = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
r_func_pos.line = l;
|
||||
r_arg_index = index;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_METHOD_NOT_FOUND;
|
||||
}
|
||||
|
||||
const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const {
|
||||
if (p_line <= 0) {
|
||||
return &class_symbol;
|
||||
|
|
|
@ -83,6 +83,8 @@ public:
|
|||
_FORCE_INLINE_ const ClassMembers &get_members() const { return members; }
|
||||
_FORCE_INLINE_ const HashMap<String, ClassMembers> &get_inner_classes() const { return inner_classes; }
|
||||
|
||||
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_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const;
|
||||
|
|
|
@ -38,7 +38,7 @@ GDScriptLanguageServer::GDScriptLanguageServer() {
|
|||
thread = NULL;
|
||||
thread_exit = false;
|
||||
_EDITOR_DEF("network/language_server/remote_port", 6008);
|
||||
_EDITOR_DEF("network/language_server/enable_smart_resolve", false);
|
||||
_EDITOR_DEF("network/language_server/enable_smart_resolve", true);
|
||||
_EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ void GDScriptTextDocument::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("hover"), &GDScriptTextDocument::hover);
|
||||
ClassDB::bind_method(D_METHOD("definition"), &GDScriptTextDocument::definition);
|
||||
ClassDB::bind_method(D_METHOD("declaration"), &GDScriptTextDocument::declaration);
|
||||
ClassDB::bind_method(D_METHOD("signatureHelp"), &GDScriptTextDocument::signatureHelp);
|
||||
ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor"), &GDScriptTextDocument::show_native_symbol_in_editor);
|
||||
}
|
||||
|
||||
|
@ -387,6 +388,20 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
|
|||
return arr;
|
||||
}
|
||||
|
||||
Variant GDScriptTextDocument::signatureHelp(const Dictionary &p_params) {
|
||||
Variant ret;
|
||||
|
||||
lsp::TextDocumentPositionParams params;
|
||||
params.load(p_params);
|
||||
|
||||
lsp::SignatureHelp s;
|
||||
if (OK == GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_signature(params, s)) {
|
||||
ret = s.to_json();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GDScriptTextDocument::GDScriptTextDocument() {
|
||||
file_checker = FileAccess::create(FileAccess::ACCESS_RESOURCES);
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
Variant hover(const Dictionary &p_params);
|
||||
Array definition(const Dictionary &p_params);
|
||||
Variant declaration(const Dictionary &p_params);
|
||||
Variant signatureHelp(const Dictionary &p_params);
|
||||
|
||||
void initialize();
|
||||
|
||||
|
|
|
@ -252,6 +252,12 @@ Error GDScriptWorkspace::initialize() {
|
|||
bool arg_default_value_started = false;
|
||||
for (int j = 0; j < data.arguments.size(); j++) {
|
||||
const DocData::ArgumentDoc &arg = data.arguments[j];
|
||||
|
||||
lsp::DocumentSymbol symbol_arg;
|
||||
symbol_arg.name = arg.name;
|
||||
symbol_arg.kind = lsp::SymbolKind::Variable;
|
||||
symbol_arg.detail = arg.type;
|
||||
|
||||
if (!arg_default_value_started && !arg.default_value.empty()) {
|
||||
arg_default_value_started = true;
|
||||
}
|
||||
|
@ -263,6 +269,8 @@ Error GDScriptWorkspace::initialize() {
|
|||
arg_str += ", ";
|
||||
}
|
||||
params += arg_str;
|
||||
|
||||
symbol.children.push_back(symbol_arg);
|
||||
}
|
||||
if (data.qualifiers.find("vararg") != -1) {
|
||||
params += params.empty() ? "..." : ", ...";
|
||||
|
@ -513,6 +521,49 @@ Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) {
|
|||
return api;
|
||||
}
|
||||
|
||||
Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature) {
|
||||
if (const ExtendGDScriptParser *parser = get_parse_result(get_file_path(p_doc_pos.textDocument.uri))) {
|
||||
|
||||
lsp::TextDocumentPositionParams text_pos;
|
||||
text_pos.textDocument = p_doc_pos.textDocument;
|
||||
|
||||
if (parser->get_left_function_call(p_doc_pos.position, text_pos.position, r_signature.activeParameter) == OK) {
|
||||
|
||||
List<const lsp::DocumentSymbol *> symbols;
|
||||
|
||||
if (const lsp::DocumentSymbol *symbol = resolve_symbol(text_pos)) {
|
||||
symbols.push_back(symbol);
|
||||
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(text_pos, symbols);
|
||||
}
|
||||
|
||||
for (List<const lsp::DocumentSymbol *>::Element *E = symbols.front(); E; E = E->next()) {
|
||||
const lsp::DocumentSymbol *symbol = E->get();
|
||||
if (symbol->kind == lsp::SymbolKind::Method || symbol->kind == lsp::SymbolKind::Function) {
|
||||
|
||||
lsp::SignatureInformation signature_info;
|
||||
signature_info.label = symbol->detail;
|
||||
signature_info.documentation = symbol->render();
|
||||
|
||||
for (int i = 0; i < symbol->children.size(); i++) {
|
||||
const lsp::DocumentSymbol &arg = symbol->children[i];
|
||||
lsp::ParameterInformation arg_info;
|
||||
arg_info.label = arg.name;
|
||||
signature_info.parameters.push_back(arg_info);
|
||||
}
|
||||
r_signature.signatures.push_back(signature_info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_signature.signatures.size()) {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERR_METHOD_NOT_FOUND;
|
||||
}
|
||||
|
||||
GDScriptWorkspace::GDScriptWorkspace() {
|
||||
ProjectSettings::get_singleton()->get_resource_path();
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
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);
|
||||
Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);
|
||||
|
||||
GDScriptWorkspace();
|
||||
~GDScriptWorkspace();
|
||||
|
|
|
@ -1402,6 +1402,120 @@ struct Hover {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a parameter of a callable-signature. A parameter can
|
||||
* have a label and a doc-comment.
|
||||
*/
|
||||
struct ParameterInformation {
|
||||
|
||||
/**
|
||||
* The label of this parameter information.
|
||||
*
|
||||
* Either a string or an inclusive start and exclusive end offsets within its containing
|
||||
* signature label. (see SignatureInformation.label). The offsets are based on a UTF-16
|
||||
* string representation as `Position` and `Range` does.
|
||||
*
|
||||
* *Note*: a label of type string should be a substring of its containing signature label.
|
||||
* Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.
|
||||
*/
|
||||
String label;
|
||||
|
||||
/**
|
||||
* The human-readable doc-comment of this parameter. Will be shown
|
||||
* in the UI but can be omitted.
|
||||
*/
|
||||
MarkupContent documentation;
|
||||
|
||||
Dictionary to_json() const {
|
||||
Dictionary dict;
|
||||
dict["label"] = label;
|
||||
dict["documentation"] = documentation.to_json();
|
||||
return dict;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the signature of something callable. A signature
|
||||
* can have a label, like a function-name, a doc-comment, and
|
||||
* a set of parameters.
|
||||
*/
|
||||
struct SignatureInformation {
|
||||
/**
|
||||
* The label of this signature. Will be shown in
|
||||
* the UI.
|
||||
*/
|
||||
String label;
|
||||
|
||||
/**
|
||||
* The human-readable doc-comment of this signature. Will be shown
|
||||
* in the UI but can be omitted.
|
||||
*/
|
||||
MarkupContent documentation;
|
||||
|
||||
/**
|
||||
* The parameters of this signature.
|
||||
*/
|
||||
Vector<ParameterInformation> parameters;
|
||||
|
||||
Dictionary to_json() const {
|
||||
Dictionary dict;
|
||||
dict["label"] = label;
|
||||
dict["documentation"] = documentation.to_json();
|
||||
Array args;
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
args.push_back(parameters[i].to_json());
|
||||
}
|
||||
dict["parameters"] = args;
|
||||
return dict;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Signature help represents the signature of something
|
||||
* callable. There can be multiple signature but only one
|
||||
* active and only one active parameter.
|
||||
*/
|
||||
struct SignatureHelp {
|
||||
/**
|
||||
* One or more signatures.
|
||||
*/
|
||||
Vector<SignatureInformation> signatures;
|
||||
|
||||
/**
|
||||
* The active signature. If omitted or the value lies outside the
|
||||
* range of `signatures` the value defaults to zero or is ignored if
|
||||
* `signatures.length === 0`. Whenever possible implementors should
|
||||
* make an active decision about the active signature and shouldn't
|
||||
* rely on a default value.
|
||||
* In future version of the protocol this property might become
|
||||
* mandatory to better express this.
|
||||
*/
|
||||
int activeSignature = 0;
|
||||
|
||||
/**
|
||||
* The active parameter of the active signature. If omitted or the value
|
||||
* lies outside the range of `signatures[activeSignature].parameters`
|
||||
* defaults to 0 if the active signature has parameters. If
|
||||
* the active signature has no parameters it is ignored.
|
||||
* In future version of the protocol this property might become
|
||||
* mandatory to better express the active parameter if the
|
||||
* active signature does have any.
|
||||
*/
|
||||
int activeParameter = 0;
|
||||
|
||||
Dictionary to_json() const {
|
||||
Dictionary dict;
|
||||
Array sigs;
|
||||
for (int i = 0; i < signatures.size(); i++) {
|
||||
sigs.push_back(signatures[i].to_json());
|
||||
}
|
||||
dict["signatures"] = sigs;
|
||||
dict["activeSignature"] = activeSignature;
|
||||
dict["activeParameter"] = activeParameter;
|
||||
return dict;
|
||||
}
|
||||
};
|
||||
|
||||
struct ServerCapabilities {
|
||||
/**
|
||||
* Defines how text documents are synced. Is either a detailed structure defining each notification or
|
||||
|
@ -1532,6 +1646,8 @@ struct ServerCapabilities {
|
|||
Dictionary dict;
|
||||
dict["textDocumentSync"] = (int)textDocumentSync.change;
|
||||
dict["completionProvider"] = completionProvider.to_json();
|
||||
signatureHelpProvider.triggerCharacters.push_back(",");
|
||||
signatureHelpProvider.triggerCharacters.push_back("(");
|
||||
dict["signatureHelpProvider"] = signatureHelpProvider.to_json();
|
||||
dict["codeLensProvider"] = false; // codeLensProvider.to_json();
|
||||
dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();
|
||||
|
|
Loading…
Reference in a new issue