From 8688259ed95c87ebcb36ce08fab9cb6088688db0 Mon Sep 17 00:00:00 2001 From: Itamar Date: Sat, 13 Mar 2021 10:37:23 +0200 Subject: [PATCH] LanguageServers/Cpp: Support jumping to declaration of preprocessor .. definitions. --- .../LanguageServers/ClientConnection.cpp | 1 + .../Cpp/ParserAutoComplete.cpp | 24 +++++++++++++++---- .../LanguageServers/Cpp/ParserAutoComplete.h | 1 + Userland/Libraries/LibCpp/Parser.cpp | 2 ++ Userland/Libraries/LibCpp/Parser.h | 8 +++++++ Userland/Libraries/LibCpp/Preprocessor.cpp | 6 +++-- Userland/Libraries/LibCpp/Preprocessor.h | 9 ++++--- Userland/Utilities/PreprocessorTest.cpp | 2 +- 8 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp b/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp index 2d249fadf63..2513ab7da49 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/ClientConnection.cpp @@ -135,6 +135,7 @@ void ClientConnection::handle(const Messages::LanguageServer::FindDeclaration& m dbgln("could not find declaration"); return; } + dbgln_if(LANGUAGE_SERVER_DEBUG, "declaration location: {} {}:{}", location.value().file, location.value().line, location.value().column); post_message(Messages::LanguageClient::DeclarationLocation(GUI::AutocompleteProvider::ProjectLocation { location.value().file, location.value().line, location.value().column })); } diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp index 9f6ac5aa49a..43dcce5d7ef 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp @@ -343,10 +343,26 @@ Optional ParserAutoComplete::find_de return {}; } auto decl = find_declaration_of(document, *node); - if (!decl) - return {}; + if (decl) + return GUI::AutocompleteProvider::ProjectLocation { decl->filename(), decl->start().line, decl->start().column }; - return GUI::AutocompleteProvider::ProjectLocation { decl->filename(), decl->start().line, decl->start().column }; + return find_preprocessor_definition(document, identifier_position); +} + +Optional ParserAutoComplete::find_preprocessor_definition(const DocumentData& document, const GUI::TextPosition& text_position) +{ + Position cpp_position { text_position.line(), text_position.column() }; + + // Search for a replaced preprocessor token that intersects with text_position + for (auto& replaced_token : document.parser().replaced_preprocessor_tokens()) { + if (replaced_token.token.start() > cpp_position) + continue; + if (replaced_token.token.end() < cpp_position) + continue; + + return GUI::AutocompleteProvider::ProjectLocation { replaced_token.preprocessor_value.filename, replaced_token.preprocessor_value.line, replaced_token.preprocessor_value.column }; + } + return {}; } RefPtr ParserAutoComplete::find_declaration_of(const DocumentData& document_data, const ASTNode& node) const @@ -409,7 +425,7 @@ OwnPtr ParserAutoComplete::create_document_dat auto document_data = make(); document_data->m_filename = move(filename); document_data->m_text = move(text); - document_data->m_preprocessor = make(document_data->text()); + document_data->m_preprocessor = make(document_data->m_filename, document_data->text()); document_data->preprocessor().process(); Preprocessor::Definitions all_definitions; diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h index 9c10b0b7c79..620afdf1c24 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.h @@ -106,6 +106,7 @@ private: String document_path_from_include_path(const StringView& include_path) const; void update_declared_symbols(const DocumentData&); GUI::AutocompleteProvider::DeclarationType type_of_declaration(const Declaration&); + Optional find_preprocessor_definition(const DocumentData&, const GUI::TextPosition&); OwnPtr create_document_data(String&& text, const String& filename); diff --git a/Userland/Libraries/LibCpp/Parser.cpp b/Userland/Libraries/LibCpp/Parser.cpp index c9f819e0a5a..d8411713a79 100644 --- a/Userland/Libraries/LibCpp/Parser.cpp +++ b/Userland/Libraries/LibCpp/Parser.cpp @@ -54,6 +54,7 @@ Parser::Parser(const StringView& program, const String& filename, Preprocessor:: } #endif } + void Parser::initialize_program_tokens(const StringView& program) { Lexer lexer(program); @@ -63,6 +64,7 @@ void Parser::initialize_program_tokens(const StringView& program) if (token.type() == Token::Type::Identifier) { if (auto defined_value = m_definitions.find(text_of_token(token)); defined_value != m_definitions.end()) { add_tokens_for_preprocessor(token, defined_value->value); + m_replaced_preprocessor_tokens.append({ token, defined_value->value }); continue; } } diff --git a/Userland/Libraries/LibCpp/Parser.h b/Userland/Libraries/LibCpp/Parser.h index 62523ddc9e2..bab5f28045b 100644 --- a/Userland/Libraries/LibCpp/Parser.h +++ b/Userland/Libraries/LibCpp/Parser.h @@ -56,6 +56,12 @@ public: Vector errors() const { return m_errors; } const Preprocessor::Definitions& definitions() const { return m_definitions; } + struct TokenAndPreprocessorDefinition { + Token token; + Preprocessor::DefinedValue preprocessor_value; + }; + const Vector& replaced_preprocessor_tokens() const { return m_replaced_preprocessor_tokens; } + private: enum class DeclarationType { Function, @@ -171,6 +177,8 @@ private: RefPtr m_root_node; NonnullRefPtrVector m_nodes; Vector m_errors; + + Vector m_replaced_preprocessor_tokens; }; } diff --git a/Userland/Libraries/LibCpp/Preprocessor.cpp b/Userland/Libraries/LibCpp/Preprocessor.cpp index 4d3fc2f82f1..ca939155218 100644 --- a/Userland/Libraries/LibCpp/Preprocessor.cpp +++ b/Userland/Libraries/LibCpp/Preprocessor.cpp @@ -31,8 +31,9 @@ #include namespace Cpp { -Preprocessor::Preprocessor(const StringView& program) - : m_program(program) +Preprocessor::Preprocessor(const String& filename, const StringView& program) + : m_filename(filename) + , m_program(program) { m_lines = m_program.split_view('\n', true); } @@ -107,6 +108,7 @@ void Preprocessor::handle_preprocessor_line(const StringView& line) consume_whitespace(); DefinedValue value; + value.filename = m_filename; value.line = m_line_index; auto string_value = lexer.consume_all(); diff --git a/Userland/Libraries/LibCpp/Preprocessor.h b/Userland/Libraries/LibCpp/Preprocessor.h index 3ae0d35083f..f1c75ecf7ea 100644 --- a/Userland/Libraries/LibCpp/Preprocessor.h +++ b/Userland/Libraries/LibCpp/Preprocessor.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include #include @@ -36,15 +37,16 @@ namespace Cpp { class Preprocessor { public: - explicit Preprocessor(const StringView&); + explicit Preprocessor(const String& filename, const StringView& program); const String& process(); const String& processed_text(); Vector included_paths() const { return m_included_paths; } struct DefinedValue { Optional value; - size_t line {0}; - size_t column {0}; + FlyString filename; + size_t line { 0 }; + size_t column { 0 }; }; using Definitions = HashMap; @@ -54,6 +56,7 @@ private: void handle_preprocessor_line(const StringView&); Definitions m_definitions; + const String m_filename; const StringView m_program; StringBuilder m_builder; Vector m_lines; diff --git a/Userland/Utilities/PreprocessorTest.cpp b/Userland/Utilities/PreprocessorTest.cpp index 77bd45a8281..e69f2cac532 100644 --- a/Userland/Utilities/PreprocessorTest.cpp +++ b/Userland/Utilities/PreprocessorTest.cpp @@ -35,6 +35,6 @@ int main(int, char**) exit(1); } auto content = file->read_all(); - Cpp::Preprocessor cpp(StringView { content }); + Cpp::Preprocessor cpp("other.h", StringView { content }); dbgln("{}", cpp.process()); }