diff options
4 files changed, 64 insertions, 1 deletions
diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp index 4f70cd9a50..f868a280dd 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.cpp @@ -8,6 +8,8 @@ #include <AK/Assertions.h> #include <AK/HashTable.h> #include <AK/OwnPtr.h> +#include <LibCore/DirIterator.h> +#include <LibCore/File.h> #include <LibCpp/AST.h> #include <LibCpp/Lexer.h> #include <LibCpp/Parser.h> @@ -64,6 +66,13 @@ Vector<GUI::AutocompleteProvider::Entry> CppComprehensionEngine::get_suggestions const auto& document = *document_ptr; auto containing_token = document.parser().token_at(position); + + if (containing_token.has_value() && containing_token->type() == Token::Type::IncludePath) { + auto results = try_autocomplete_include(document, containing_token.value()); + if (results.has_value()) + return results.value(); + } + auto node = document.parser().node_at(position); if (!node) { dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", position.line, position.column); @@ -540,6 +549,7 @@ OwnPtr<CppComprehensionEngine::DocumentData> CppComprehensionEngine::create_docu document_data->m_text = move(text); document_data->m_preprocessor = make<Preprocessor>(document_data->m_filename, document_data->text()); document_data->preprocessor().set_ignore_unsupported_keywords(true); + document_data->preprocessor().set_keep_include_statements(true); document_data->preprocessor().process(); Preprocessor::Definitions preprocessor_definitions; @@ -590,4 +600,44 @@ String CppComprehensionEngine::scope_of_declaration(const Declaration& decl) con return String::formatted("{}::{}", parent_scope, containing_scope); } +Optional<Vector<GUI::AutocompleteProvider::Entry>> CppComprehensionEngine::try_autocomplete_include(const DocumentData&, Token include_path_token) +{ + VERIFY(include_path_token.type() == Token::Type::IncludePath); + auto partial_include = include_path_token.text().trim_whitespace(); + + String include_root; + auto include_type = GUI::AutocompleteProvider::CompletionKind::ProjectInclude; + if (partial_include.starts_with("<")) { + include_root = "/usr/include/"; + include_type = GUI::AutocompleteProvider::CompletionKind::SystemInclude; + } else if (partial_include.starts_with("\"")) { + include_root = filedb().project_root(); + } else + return {}; + + auto last_slash = partial_include.find_last_of("/"); + auto include_dir = String::empty(); + auto partial_basename = partial_include.substring_view((last_slash.has_value() ? last_slash.value() : 0) + 1); + if (last_slash.has_value()) { + include_dir = partial_include.substring_view(1, last_slash.value()); + } + + auto full_dir = String::formatted("{}{}", include_root, include_dir); + dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "searching path: {}, partial_basename: {}", full_dir, partial_basename); + + Core::DirIterator it(full_dir, Core::DirIterator::Flags::SkipDots); + Vector<GUI::AutocompleteProvider::Entry> options; + + while (it.has_next()) { + auto path = it.next_path(); + if (!(path.ends_with(".h") || Core::File::is_directory(LexicalPath::join(full_dir, path).string()))) + continue; + if (path.starts_with(partial_basename)) { + options.append({ path, partial_basename.length(), include_type, GUI::AutocompleteProvider::Language::Cpp }); + } + } + + return options; +} + } diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h index 5072f45cd1..dfbfaf0c43 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/CppComprehensionEngine.h @@ -103,6 +103,7 @@ private: OwnPtr<DocumentData> create_document_data(String&& text, const String& filename); Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_property(const DocumentData&, const ASTNode&, Optional<Token> containing_token) const; Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_name(const DocumentData&, const ASTNode&, Optional<Token> containing_token) const; + Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_include(const DocumentData&, Token include_path_token); HashMap<String, OwnPtr<DocumentData>> m_documents; }; diff --git a/Userland/Libraries/LibGUI/AutocompleteProvider.cpp b/Userland/Libraries/LibGUI/AutocompleteProvider.cpp index b0fc9ae2bb..bc6d8162f1 100644 --- a/Userland/Libraries/LibGUI/AutocompleteProvider.cpp +++ b/Userland/Libraries/LibGUI/AutocompleteProvider.cpp @@ -176,7 +176,17 @@ void AutocompleteBox::apply_suggestion() size_t partial_length = suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::PartialInputLength).to_i64(); VERIFY(suggestion.length() >= partial_length); - auto completion = suggestion.substring_view(partial_length, suggestion.length() - partial_length); + auto completion_view = suggestion.substring_view(partial_length, suggestion.length() - partial_length); + auto completion_kind = (GUI::AutocompleteProvider::CompletionKind)suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::Kind).as_uint(); + + String completion; + if (completion_view.ends_with(".h") && completion_kind == GUI::AutocompleteProvider::CompletionKind::SystemInclude) + completion = String::formatted("{}{}", completion_view, ">"); + else if (completion_view.ends_with(".h") && completion_kind == GUI::AutocompleteProvider::CompletionKind::ProjectInclude) + completion = String::formatted("{}{}", completion_view, "\""); + else + completion = completion_view; + m_editor->insert_at_cursor_or_replace_selection(completion); } diff --git a/Userland/Libraries/LibGUI/AutocompleteProvider.h b/Userland/Libraries/LibGUI/AutocompleteProvider.h index 266eae132d..4a455f89a7 100644 --- a/Userland/Libraries/LibGUI/AutocompleteProvider.h +++ b/Userland/Libraries/LibGUI/AutocompleteProvider.h @@ -23,6 +23,8 @@ public: enum class CompletionKind { Identifier, PreprocessorDefinition, + SystemInclude, + ProjectInclude, }; enum class Language { |