summaryrefslogtreecommitdiff
path: root/Userland/DevTools
diff options
context:
space:
mode:
authorItamar <itamar8910@gmail.com>2021-05-15 22:13:32 +0300
committerAndreas Kling <kling@serenityos.org>2021-05-15 23:28:50 +0200
commitc54238f65cefbbf1751c8f124baef6a546773e6b (patch)
tree79fdafc8191524c7fc6f439bedf6021f44d94b59 /Userland/DevTools
parent0e51aea78140c99aacbec5c0fbaddb01c8c2a622 (diff)
downloadserenity-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/DevTools')
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp105
-rw-r--r--Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h5
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;
};