-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for deprecated symbols to language server and script editor #100019
base: master
Are you sure you want to change the base?
Conversation
It seems like just about everything is working, except for global variable names for autoloads. As stated above: Warning Autoload global variable names will not display as deprecated. # deprecated_autoload.gd
extends Node
## This autoload is registered with the name DeprecatedAutoload,
## and is marked as deprecated.
## @deprecated
func do_thing():
print("a thing has been done") # main.gd
func _ready():
# DeprecatedAutoload will *not* show as deprecated
DeprecatedAu... I've spent a fair amount of time this evening trying to figure out how to get to the What are others' thoughts about not displaying global variable names for autoloads as deprecated even if they're marked as deprecated in their GDScript file's documentation comments? |
From what I see autoload doc data is also registered in EditorHelp::get_doc_data().class_list under the name of the autoload, but the update behavior seems very flaky so I'm not sure whether we can rely on it. Besides that the only way I know of would be getting the path of the autoload, loading the script and getting the info from the script. Would need some testing regarding performance impact though, since in the worst case autocompletion gets called with every keystroke so we need to make sure that the resource loader caches the script and doc. In general before merging we need to test this PR for performance. We already have the problem, that autocompletion can trigger parsing and analyzing of a lot of scripts with each keystroke. This PR shouldn't add more scripts to that list. |
I'm finding this to be shaky as well. I have a test project that I'm using, and can do the following:
Indeed, using the following snippet: for (const KeyValue<String, DocData::ClassDoc> &E2 : EditorHelp::get_doc_data()->class_list) {
print_line(vformat("Class %s found", E2.key));
} I do get the following output:
(where And indeed, if I delete So it seems to me like something being cached in Regarding performance, I agree that's definitely a big concern, and one I've been conscious of as I've been working through putting this feature together. Across the file, there are a few ways I've gotten deprecated data from the docs: When I have the For checking if a class is deprecated, I have to get So far, I'm most nervous about when I want to check whether signals/properties/methods on some class are deprecated. My process so far has been to:
In practice, that looks like this: // Up at the top of the method, so it can be reused:
const HashMap<String, DocData::ClassDoc> class_doc_map = EditorHelp::get_doc_data()->class_list;
// ...
// Here we're going to create the list of constants to provide as autocomplete suggestions.
HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
// Create a HashMap mapping constants' names to their deprecated status.
HashMap<StringName, bool> const_is_deprecated_map;
for (const DocData::ConstantDoc &constant_doc : class_doc_map.get(base_type.class_type->get_global_name()).constants) {
const_is_deprecated_map.insert(constant_doc.name, constant_doc.is_deprecated);
}
for (const KeyValue<StringName, Variant> &E : constants) {
int location = p_recursion_depth + _get_constant_location(scr, E.key);
ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
// Add the constant's deprecated status if we have it.
if (const_is_deprecated_map.has(E.key.operator String())) {
option.deprecated = const_is_deprecated_map.get(E.key.operator String());
}
r_result.insert(option.display, option);
} Without changing how the doc data is structured, I'm not sure how this could be made much more efficient. These hashmaps could perhaps be computed earlier on, alongside the rest of the doc data, and then cached? Although then that introduces multiple sources of truth for deprecated statuses, which isn't ideal. |
fcace42
to
120e7b4
Compare
I wouldn't worry about hashmap lookups and such, that should be fine. Just triggering parsing and analyzing for a script can get very expensive. |
r_result.insert(option.display, option); | ||
} | ||
} break; | ||
case GDScriptParser::ClassNode::Member::CONSTANT: { | ||
if (member.constant->get_datatype().is_meta_type && p_context.current_class->outer != nullptr) { | ||
// TODO: Describe this case and its examples. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO: Describe this case and its examples. | |
// Preloaded type within this script. | |
// const MyType = preload("res://my_type.gd) | |
// var m: MyT... # <-- will suggest MyType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah forgot, it's broken on master
. #94996 would fix it but I can't get any reviews it seems :(
Funnily enough the PR fixes this in a different code path. Might need to investigate this one again. My bad I fixed a similar case in another place but this one as well. Just from looking at the code it should work in inner classes only at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the behaviors I tried to verify with tests but wasn't able to. Specifically, it seemed like _find_enumeration_candidates
was never being triggered. They may well be bugs we should file separate issues for.
r_result.insert(option.display, option); | ||
} | ||
} break; | ||
case GDScriptParser::ClassNode::Member::CONSTANT: { | ||
if (member.constant->get_datatype().is_meta_type && p_context.current_class->outer != nullptr) { | ||
// TODO: Describe this case and its examples. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b368813
to
6d9a9fe
Compare
6d9a9fe
to
8310299
Compare
There was an issue where I was attempting to access editor theming, and causing some of the CI to fail, but I think I found a fix for that. If all the checks don't pass, I'll try to continue working through them on another branch so that they put less strain on the main repo's CI resources 😅 |
Just a note that after #91060 local variables and constants can also be deprecated/experimental. Not sure how useful this is in practice and whether LSP supports deprecating local identifiers, but it would be nice to add it. |
For me, practically speaking, it doesn't make sense for a local variable to be deprecated - if so then I feel like it's time for a refactor of your code 😅 But, if documentation comments are parsed for local identifiers and that includes deprecated status, I also agree that for consistency's sake it should be added. And added it is! I may not push it to the branch that this PR is tracking immediately, as I'd like to get the CI issues resolved first. |
@@ -1209,6 +1294,16 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base | |||
if (!base_type.is_meta_type) { | |||
List<PropertyInfo> members; | |||
scr->get_script_property_list(&members); | |||
|
|||
HashSet<StringName> deprecated_properties; | |||
if (base_type.class_type && class_doc_map.has(base_type.class_type->get_global_name())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you shouldn't use get_global_name()
to determine the doc class name. Because for unnamed classes, inner classes and autoload singletons the name may differ from the global name.
After #91060 there is a new method Script::get_doc_class_name()
, but there is no counterpart for GDScriptParser::ClassNode
. I reused GDScriptDocGen::doctype_from_gdtype()
(which uses GDScriptDocGen::_get_class_name()
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I'll look more into this once I have the compatibility method stuff working and CI tests passing.
CodeCompletionOptions need to be given the correct deprecated status still Variables marked as deprecated are recognized Attempt to get deprecated info about GDScript functions Consult DocData for more things being marked deprecated @GDscript methods and other things work now! Support for methods on built-in classes Add support for enums, and clean up some of the code to not use iterators when not necessary Added more deprecated checks Attempt to understand named enum property documentation Display deprecated functions with strikethrough in built-in script editor Named enum members now working! Recognize deprecated built-in classes when used as types Add deprecated checks for some spots, while trying to find autoload spot Added better TODO note Autoloads do technically kinda work, but only if you clear .godot
…ld debugging code [no ci]
…attempting to use it
…r `add_code_completion_option`
I'm currently working on getting the CI checks to pass. By adding the In void CodeEdit::_add_code_completion_option_compat_100019(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const Ref<Resource> &p_icon, const Variant &p_value, int p_location) {
add_code_completion_option(p_type, p_display_text, p_insert_text, p_text_color, p_icon, p_value, p_location, false);
}
void CodeEdit::_bind_compatibility_methods() {
// ...
ClassDB::bind_compatibility_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value", "location"), &CodeEdit::_add_code_completion_option_compat_100019, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL), DEFVAL(LOCATION_OTHER), DEFVAL(false));
} In #ifndef DISABLE_DEPRECATED
// ...
void _add_code_completion_option_compat_100019(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const Ref<Resource> &p_icon = Ref<Resource>(), const Variant &p_value = Variant::NIL, int p_location = LOCATION_OTHER);
static void _bind_compatibility_methods();
#endif And, in
However I'm still getting the error:
I feel like I've followed all of the steps at https://docs.godotengine.org/en/stable/contributing/development/handling_compatibility_breakages.html, so I'm feeling a bit out of ideas at the moment... 😅 |
Continue to try to get compatibility method to work Avoid making changes to original compatibility method Maybe it will work this time?? Please Add another API compatibility check thing
f0f34f4
to
d9e37ed
Compare
Related to godotengine/godot-proposals#11079 !
This PR adds the ability for code completions from the language server to be marked as deprecated, according to the class documentation, so that they can be displayed as such in a code editor. It also implements this display for the built-in script editor:
In Godot's built-in script editor:
In VS Code:
Note
While the functionality is mostly working, the code itself is still in a messy WIP state. I'm definitely planning on improving its structure (and squashing commits, etc) before opening the full pull request. That being said, if you see patterns or things in the code that could be improved, please let me know!
Tested cases
Methods
A user-defined method marked with `@deprecated`
A class built-in to Godot and marked as deprecated in the documentation
Classes
Main class of a file marked with `@deprecated`
Inner class marked with `@deprecated`
A class built-in to Godot and marked as deprecated in the documentation
A class built-in to Godot and marked as deprecated in the documentation, used as a type
An autoload with a global variable name
Enums and constants
A user-defined constant, marked with `@deprecated`
A user-defined named enum, marked with `@deprecated`
Individual values in a user-defined anonymous enum, marked with `@deprecated`
Individual values in a user-defined named enum, marked with `@deprecated`
Individual values in an enum from a class built-in to Godot and marked as deprecated in the documentation
Constants in `@GlobalScope`
Properties
A user-defined property marked with `@deprecated`
A class built-in to Godot and marked as deprecated in the documentation
Signals
A user-defined signal marked with `@deprecated`
A class built-in to Godot and marked as deprecated in the documentation
To-Do
Language server
DocData
)Godot Script editor