diff options
author | Itamar <itamar8910@gmail.com> | 2021-05-15 22:13:32 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-15 23:28:50 +0200 |
commit | c54238f65cefbbf1751c8f124baef6a546773e6b (patch) | |
tree | 79fdafc8191524c7fc6f439bedf6021f44d94b59 /Userland | |
parent | 0e51aea78140c99aacbec5c0fbaddb01c8c2a622 (diff) | |
download | serenity-c54238f65cefbbf1751c8f124baef6a546773e6b.zip |
CppLanguageServer: Make autocomplete logic consider scopes
When returning autocomplete suggestions, we now consider the scope of
the name that is being completed.
For example, when requested to complete an expression like
'MyNamespace::', we will only suggest things that are in the
'MyNamespace' namespace.
This commit also has some general refactoring of the autocomplete
logic.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp | 105 | ||||
-rw-r--r-- | Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h | 5 |
2 files changed, 86 insertions, 24 deletions
diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp index 1b02938386..ee6e5bef4b 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp @@ -63,32 +63,56 @@ Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::get_suggestions(con return {}; const auto& document = *document_ptr; + auto containing_token = document.parser().token_at(position); auto node = document.parser().node_at(position); if (!node) { dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", position.line, position.column); return {}; } - if (node->is_identifier()) { - if (is_property(*node)) { - return autocomplete_property(document, (MemberExpression&)(*node->parent()), document.parser().text_of_node(*node)); - } + if (node->parent() && node->parent()->parent()) + dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "node: {}, parent: {}, grandparent: {}", node->class_name(), node->parent()->class_name(), node->parent()->parent()->class_name()); - return autocomplete_name(document, *node, document.parser().text_of_node(*node)); - } + if (!node->parent()) + return {}; + + auto results = autocomplete_property(document, *node, containing_token); + if (results.has_value()) + return results.value(); - if (is_empty_property(document, *node, position)) { - VERIFY(node->parent()->is_member_expression()); - return autocomplete_property(document, (MemberExpression&)(*node->parent()), ""); + results = autocomplete_name(document, *node, containing_token); + if (results.has_value()) + return results.value(); + return {}; +} + +Optional<Vector<GUI::AutocompleteProvider::Entry>> ParserAutoComplete::autocomplete_name(const DocumentData& document, const ASTNode& node, Optional<Token> containing_token) const +{ + auto partial_text = String::empty(); + if (containing_token.has_value() && containing_token.value().type() != Token::Type::ColonColon) { + partial_text = containing_token.value().text(); } + return autocomplete_name(document, node, partial_text); +} - String partial_text = String::empty(); - auto containing_token = document.parser().token_at(position); - if (containing_token.has_value()) { - partial_text = document.parser().text_of_token(containing_token.value()); +Optional<Vector<GUI::AutocompleteProvider::Entry>> ParserAutoComplete::autocomplete_property(const DocumentData& document, const ASTNode& node, Optional<Token> containing_token) const +{ + if (!containing_token.has_value()) + return {}; + + if (!node.parent()->is_member_expression()) + return {}; + + const auto& parent = static_cast<const MemberExpression&>(*node.parent()); + + auto partial_text = String::empty(); + if (containing_token.value().type() != Token::Type::Dot) { + if (&node != parent.m_property) + return {}; + partial_text = containing_token.value().text(); } - return autocomplete_name(document, *node, partial_text.view()); + return autocomplete_property(document, parent, partial_text); } NonnullRefPtrVector<Declaration> ParserAutoComplete::get_available_declarations(const DocumentData& document, const ASTNode& node, RecurseIntoScopes recurse_into_scopes) const @@ -106,28 +130,38 @@ NonnullRefPtrVector<Declaration> ParserAutoComplete::get_available_declarations( Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::autocomplete_name(const DocumentData& document, const ASTNode& node, const String& partial_text) const { + auto target_scope = scope_of_name_or_identifier(node); + auto available_declarations = get_available_declarations(document, node, RecurseIntoScopes::No); + Vector<StringView> available_names; - auto add_name = [&available_names](auto& name) { + auto add_if_valid = [this, &available_names, &target_scope](auto& decl) { + auto name = decl.m_name; if (name.is_null() || name.is_empty()) return; + auto scope = scope_of_declaration(decl); + if (scope.is_null()) + scope = String::empty(); + if (scope != target_scope) + return; if (!available_names.contains_slow(name)) available_names.append(name); }; + for (auto& decl : available_declarations) { if (decl.filename() == node.filename() && decl.start().line > node.start().line) continue; if (decl.is_variable_or_parameter_declaration()) { - add_name(((Cpp::VariableOrParameterDeclaration&)decl).m_name); + add_if_valid(decl); } if (decl.is_struct_or_class()) { - add_name(((Cpp::StructOrClassDeclaration&)decl).m_name); + add_if_valid(decl); } if (decl.is_function()) { - add_name(((Cpp::FunctionDeclaration&)decl).m_name); + add_if_valid(decl); } if (decl.is_namespace()) { - add_name(((Cpp::NamespaceDeclaration&)decl).m_name); + add_if_valid(decl); } } @@ -138,15 +172,40 @@ Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::autocomplete_name(c } } - for (auto& preprocessor_name : document.parser().preprocessor_definitions().keys()) { - if (preprocessor_name.starts_with(partial_text)) { - suggestions.append({ preprocessor_name.to_string(), partial_text.length(), GUI::AutocompleteProvider::CompletionKind::PreprocessorDefinition }); + if (target_scope.is_empty()) { + for (auto& preprocessor_name : document.parser().preprocessor_definitions().keys()) { + if (preprocessor_name.starts_with(partial_text)) { + suggestions.append({ preprocessor_name.to_string(), partial_text.length(), GUI::AutocompleteProvider::CompletionKind::PreprocessorDefinition }); + } } } return suggestions; } +String ParserAutoComplete::scope_of_name_or_identifier(const ASTNode& node) const +{ + const Name* name = nullptr; + if (node.is_name()) { + name = reinterpret_cast<const Name*>(&node); + } else if (node.is_identifier()) { + auto* parent = node.parent(); + if (!(parent && parent->is_name())) + return {}; + name = reinterpret_cast<const Name*>(parent); + } else { + return String::empty(); + } + + VERIFY(name->is_name()); + + Vector<StringView> scope_parts; + for (auto& scope_part : name->m_scope) { + scope_parts.append(scope_part.m_name); + } + return String::join("::", scope_parts); +} + Vector<GUI::AutocompleteProvider::Entry> ParserAutoComplete::autocomplete_property(const DocumentData& document, const MemberExpression& parent, const String partial_text) const { auto type = type_of(document, *parent.m_object); @@ -507,7 +566,7 @@ OwnPtr<ParserAutoComplete::DocumentData> ParserAutoComplete::create_document_dat return document_data; } -String ParserAutoComplete::scope_of_declaration(const Declaration& decl) +String ParserAutoComplete::scope_of_declaration(const Declaration& decl) const { auto parent = decl.parent(); if (!parent) diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h index c8742e0d51..c3b735d948 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h @@ -96,10 +96,13 @@ private: String document_path_from_include_path(const StringView& include_path) const; void update_declared_symbols(DocumentData&); GUI::AutocompleteProvider::DeclarationType type_of_declaration(const Declaration&); - String scope_of_declaration(const Declaration&); + String scope_of_declaration(const Declaration&) const; + String scope_of_name_or_identifier(const ASTNode& node) const; Optional<GUI::AutocompleteProvider::ProjectLocation> find_preprocessor_definition(const DocumentData&, const GUI::TextPosition&); OwnPtr<DocumentData> create_document_data(String&& text, const String& filename); + Optional<Vector<GUI::AutocompleteProvider::Entry>> autocomplete_property(const DocumentData&, const ASTNode&, Optional<Token> containing_token) const; + Optional<Vector<GUI::AutocompleteProvider::Entry>> autocomplete_name(const DocumentData&, const ASTNode&, Optional<Token> containing_token) const; HashMap<String, OwnPtr<DocumentData>> m_documents; }; |