mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 10:07:44 +00:00
LibCodeComprehension: Re-organize code comprehension related code
This moves all code comprehension-related code to a new library, LibCodeComprehension. This also moves some types related to code comprehension tasks (such as autocomplete, find declaration) out of LibGUI and into LibCodeComprehension.
This commit is contained in:
parent
a2c34554cd
commit
b35293d945
65 changed files with 685 additions and 491 deletions
|
@ -16,7 +16,7 @@
|
|||
namespace IPC {
|
||||
|
||||
template<>
|
||||
inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::Entry& response)
|
||||
inline bool encode(Encoder& encoder, CodeComprehension::AutocompleteResultEntry const& response)
|
||||
{
|
||||
encoder << response.completion;
|
||||
encoder << response.partial_input_length;
|
||||
|
@ -27,7 +27,7 @@ inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::Entry& res
|
|||
}
|
||||
|
||||
template<>
|
||||
inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::Entry& response)
|
||||
inline ErrorOr<void> decode(Decoder& decoder, CodeComprehension::AutocompleteResultEntry& response)
|
||||
{
|
||||
TRY(decoder.decode(response.completion));
|
||||
TRY(decoder.decode(response.partial_input_length));
|
||||
|
@ -38,7 +38,7 @@ inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::Entry&
|
|||
}
|
||||
|
||||
template<>
|
||||
inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::ProjectLocation& location)
|
||||
inline bool encode(Encoder& encoder, CodeComprehension::ProjectLocation const& location)
|
||||
{
|
||||
encoder << location.file;
|
||||
encoder << location.line;
|
||||
|
@ -47,7 +47,7 @@ inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::ProjectLoc
|
|||
}
|
||||
|
||||
template<>
|
||||
inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::ProjectLocation& location)
|
||||
inline ErrorOr<void> decode(Decoder& decoder, CodeComprehension::ProjectLocation& location)
|
||||
{
|
||||
TRY(decoder.decode(location.file));
|
||||
TRY(decoder.decode(location.line));
|
||||
|
@ -56,7 +56,7 @@ inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::Project
|
|||
}
|
||||
|
||||
template<>
|
||||
inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::Declaration& declaration)
|
||||
inline bool encode(Encoder& encoder, CodeComprehension::Declaration const& declaration)
|
||||
{
|
||||
encoder << declaration.name;
|
||||
if (!encode(encoder, declaration.position))
|
||||
|
@ -67,7 +67,7 @@ inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::Declaratio
|
|||
}
|
||||
|
||||
template<>
|
||||
inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::Declaration& declaration)
|
||||
inline ErrorOr<void> decode(Decoder& decoder, CodeComprehension::Declaration& declaration)
|
||||
{
|
||||
TRY(decoder.decode(declaration.name));
|
||||
TRY(decoder.decode(declaration.position));
|
||||
|
@ -77,7 +77,7 @@ inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::Declara
|
|||
}
|
||||
|
||||
template<>
|
||||
inline bool encode(Encoder& encoder, Cpp::Parser::TodoEntry const& entry)
|
||||
inline bool encode(Encoder& encoder, CodeComprehension::TodoEntry const& entry)
|
||||
{
|
||||
encoder << entry.content;
|
||||
encoder << entry.filename;
|
||||
|
@ -87,7 +87,7 @@ inline bool encode(Encoder& encoder, Cpp::Parser::TodoEntry const& entry)
|
|||
}
|
||||
|
||||
template<>
|
||||
inline ErrorOr<void> decode(Decoder& decoder, Cpp::Parser::TodoEntry& entry)
|
||||
inline ErrorOr<void> decode(Decoder& decoder, CodeComprehension::TodoEntry& entry)
|
||||
{
|
||||
TRY(decoder.decode(entry.content));
|
||||
TRY(decoder.decode(entry.filename));
|
||||
|
@ -97,7 +97,7 @@ inline ErrorOr<void> decode(Decoder& decoder, Cpp::Parser::TodoEntry& entry)
|
|||
}
|
||||
|
||||
template<>
|
||||
inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::TokenInfo& location)
|
||||
inline bool encode(Encoder& encoder, CodeComprehension::TokenInfo const& location)
|
||||
{
|
||||
encoder << (u32)location.type;
|
||||
static_assert(sizeof(location.type) == sizeof(u32));
|
||||
|
@ -109,13 +109,13 @@ inline bool encode(Encoder& encoder, const GUI::AutocompleteProvider::TokenInfo&
|
|||
}
|
||||
|
||||
template<>
|
||||
inline ErrorOr<void> decode(Decoder& decoder, GUI::AutocompleteProvider::TokenInfo& entry)
|
||||
inline ErrorOr<void> decode(Decoder& decoder, CodeComprehension::TokenInfo& entry)
|
||||
{
|
||||
u32 semantic_type { 0 };
|
||||
static_assert(sizeof(semantic_type) == sizeof(entry.type));
|
||||
|
||||
TRY(decoder.decode(semantic_type));
|
||||
entry.type = static_cast<GUI::AutocompleteProvider::TokenInfo::SemanticType>(semantic_type);
|
||||
entry.type = static_cast<CodeComprehension::TokenInfo::SemanticType>(semantic_type);
|
||||
TRY(decoder.decode(entry.start_line));
|
||||
TRY(decoder.decode(entry.start_column));
|
||||
TRY(decoder.decode(entry.end_line));
|
||||
|
|
|
@ -101,16 +101,16 @@ ClassViewModel::ClassViewModel()
|
|||
{
|
||||
m_root_scope.clear();
|
||||
ProjectDeclarations::the().for_each_declared_symbol([this](auto& decl) {
|
||||
if (decl.type == GUI::AutocompleteProvider::DeclarationType::Class
|
||||
|| decl.type == GUI::AutocompleteProvider::DeclarationType::Struct
|
||||
|| decl.type == GUI::AutocompleteProvider::DeclarationType::Member
|
||||
|| decl.type == GUI::AutocompleteProvider::DeclarationType::Namespace) {
|
||||
if (decl.type == CodeComprehension::DeclarationType::Class
|
||||
|| decl.type == CodeComprehension::DeclarationType::Struct
|
||||
|| decl.type == CodeComprehension::DeclarationType::Member
|
||||
|| decl.type == CodeComprehension::DeclarationType::Namespace) {
|
||||
add_declaration(decl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static ClassViewNode& add_child_node(NonnullOwnPtrVector<ClassViewNode>& children, NonnullOwnPtr<ClassViewNode>&& node_ptr, ClassViewNode* parent, const GUI::AutocompleteProvider::Declaration* declaration)
|
||||
static ClassViewNode& add_child_node(NonnullOwnPtrVector<ClassViewNode>& children, NonnullOwnPtr<ClassViewNode>&& node_ptr, ClassViewNode* parent, CodeComprehension::Declaration const* declaration)
|
||||
{
|
||||
node_ptr->parent = parent;
|
||||
node_ptr->declaration = declaration;
|
||||
|
@ -127,7 +127,7 @@ static ClassViewNode& add_child_node(NonnullOwnPtrVector<ClassViewNode>& childre
|
|||
return children.at(inserted_index);
|
||||
}
|
||||
|
||||
void ClassViewModel::add_declaration(const GUI::AutocompleteProvider::Declaration& decl)
|
||||
void ClassViewModel::add_declaration(CodeComprehension::Declaration const& decl)
|
||||
{
|
||||
ClassViewNode* parent = nullptr;
|
||||
auto scope_parts = decl.scope.view().split_view("::");
|
||||
|
|
|
@ -31,7 +31,7 @@ private:
|
|||
// This is currently achieved with the on_update callback of ProjectDeclarations.
|
||||
struct ClassViewNode {
|
||||
StringView name;
|
||||
const GUI::AutocompleteProvider::Declaration* declaration { nullptr };
|
||||
CodeComprehension::Declaration const* declaration { nullptr };
|
||||
NonnullOwnPtrVector<ClassViewNode> children;
|
||||
ClassViewNode* parent { nullptr };
|
||||
|
||||
|
@ -50,7 +50,7 @@ public:
|
|||
|
||||
private:
|
||||
explicit ClassViewModel();
|
||||
void add_declaration(const GUI::AutocompleteProvider::Declaration&);
|
||||
void add_declaration(CodeComprehension::Declaration const&);
|
||||
NonnullOwnPtrVector<ClassViewNode> m_root_scope;
|
||||
};
|
||||
|
||||
|
|
|
@ -497,7 +497,7 @@ Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data(
|
|||
return Editor::AutoCompleteRequestData { cursor() };
|
||||
}
|
||||
|
||||
void Editor::LanguageServerAidedAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)> callback)
|
||||
void Editor::LanguageServerAidedAutocompleteProvider::provide_completions(Function<void(Vector<CodeComprehension::AutocompleteResultEntry>)> callback)
|
||||
{
|
||||
auto& editor = static_cast<Editor&>(*m_editor).wrapper().editor();
|
||||
auto data = editor.get_autocomplete_request_data();
|
||||
|
@ -655,7 +655,7 @@ void Editor::set_language_client_for(CodeDocument const& document)
|
|||
m_language_client = get_language_client<LanguageClients::Shell::ConnectionToServer>(project().root_path());
|
||||
|
||||
if (m_language_client) {
|
||||
m_language_client->on_tokens_info_result = [this](Vector<GUI::AutocompleteProvider::TokenInfo> const& tokens_info) {
|
||||
m_language_client->on_tokens_info_result = [this](Vector<CodeComprehension::TokenInfo> const& tokens_info) {
|
||||
on_tokens_info_result(tokens_info);
|
||||
};
|
||||
}
|
||||
|
@ -728,7 +728,7 @@ void Editor::on_token_info_timer_tick()
|
|||
m_language_client->get_tokens_info(code_document().file_path());
|
||||
}
|
||||
|
||||
void Editor::on_tokens_info_result(Vector<GUI::AutocompleteProvider::TokenInfo> const& tokens_info)
|
||||
void Editor::on_tokens_info_result(Vector<CodeComprehension::TokenInfo> const& tokens_info)
|
||||
{
|
||||
auto highlighter = syntax_highlighter();
|
||||
if (highlighter && highlighter->is_cpp_semantic_highlighter()) {
|
||||
|
|
|
@ -92,7 +92,7 @@ private:
|
|||
virtual ~LanguageServerAidedAutocompleteProvider() override { }
|
||||
|
||||
private:
|
||||
virtual void provide_completions(Function<void(Vector<Entry>)> callback) override;
|
||||
virtual void provide_completions(Function<void(Vector<CodeComprehension::AutocompleteResultEntry>)> callback) override;
|
||||
LanguageClient& m_language_client;
|
||||
};
|
||||
|
||||
|
@ -104,7 +104,7 @@ private:
|
|||
void set_autocomplete_provider_for(CodeDocument const&);
|
||||
void handle_function_parameters_hint_request();
|
||||
void on_token_info_timer_tick();
|
||||
void on_tokens_info_result(Vector<GUI::AutocompleteProvider::TokenInfo> const& tokens_info);
|
||||
void on_tokens_info_result(Vector<CodeComprehension::TokenInfo> const& tokens_info);
|
||||
void create_tokens_info_timer();
|
||||
ErrorOr<void> initialize_documentation_tooltip();
|
||||
ErrorOr<void> initialize_parameters_hint_tooltip();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace HackStudio {
|
||||
|
||||
void ConnectionToServer::auto_complete_suggestions(Vector<GUI::AutocompleteProvider::Entry> const& suggestions)
|
||||
void ConnectionToServer::auto_complete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> const& suggestions)
|
||||
{
|
||||
if (!m_current_language_client) {
|
||||
dbgln("Language Server connection has no attached language client");
|
||||
|
@ -23,7 +23,7 @@ void ConnectionToServer::auto_complete_suggestions(Vector<GUI::AutocompleteProvi
|
|||
m_current_language_client->provide_autocomplete_suggestions(suggestions);
|
||||
}
|
||||
|
||||
void ConnectionToServer::declaration_location(const GUI::AutocompleteProvider::ProjectLocation& location)
|
||||
void ConnectionToServer::declaration_location(CodeComprehension::ProjectLocation const& location)
|
||||
{
|
||||
if (!m_current_language_client) {
|
||||
dbgln("Language Server connection has no attached language client");
|
||||
|
@ -43,7 +43,7 @@ void ConnectionToServer::parameters_hint_result(Vector<String> const& params, in
|
|||
m_current_language_client->parameters_hint_result(params, static_cast<size_t>(argument_index));
|
||||
}
|
||||
|
||||
void ConnectionToServer::tokens_info_result(Vector<GUI::AutocompleteProvider::TokenInfo> const& tokens_info)
|
||||
void ConnectionToServer::tokens_info_result(Vector<CodeComprehension::TokenInfo> const& tokens_info)
|
||||
{
|
||||
if (!m_current_language_client) {
|
||||
dbgln("Language Server connection has no attached language client");
|
||||
|
@ -93,10 +93,10 @@ void LanguageClient::request_autocomplete(String const& path, size_t cursor_line
|
|||
if (!m_connection_wrapper.connection())
|
||||
return;
|
||||
set_active_client();
|
||||
m_connection_wrapper.connection()->async_auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation { path, cursor_line, cursor_column });
|
||||
m_connection_wrapper.connection()->async_auto_complete_suggestions(CodeComprehension::ProjectLocation { path, cursor_line, cursor_column });
|
||||
}
|
||||
|
||||
void LanguageClient::provide_autocomplete_suggestions(Vector<GUI::AutocompleteProvider::Entry> const& suggestions) const
|
||||
void LanguageClient::provide_autocomplete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> const& suggestions) const
|
||||
{
|
||||
if (on_autocomplete_suggestions)
|
||||
on_autocomplete_suggestions(suggestions);
|
||||
|
@ -120,12 +120,12 @@ bool LanguageClient::is_active_client() const
|
|||
|
||||
HashMap<String, NonnullOwnPtr<ConnectionToServerWrapper>> ConnectionToServerInstances::s_instance_for_language;
|
||||
|
||||
void ConnectionToServer::declarations_in_document(String const& filename, Vector<GUI::AutocompleteProvider::Declaration> const& declarations)
|
||||
void ConnectionToServer::declarations_in_document(String const& filename, Vector<CodeComprehension::Declaration> const& declarations)
|
||||
{
|
||||
ProjectDeclarations::the().set_declared_symbols(filename, declarations);
|
||||
}
|
||||
|
||||
void ConnectionToServer::todo_entries_in_document(String const& filename, Vector<Cpp::Parser::TodoEntry> const& todo_entries)
|
||||
void ConnectionToServer::todo_entries_in_document(String const& filename, Vector<CodeComprehension::TodoEntry> const& todo_entries)
|
||||
{
|
||||
ToDoEntries::the().set_entries(filename, move(todo_entries));
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ void LanguageClient::search_declaration(String const& path, size_t line, size_t
|
|||
if (!m_connection_wrapper.connection())
|
||||
return;
|
||||
set_active_client();
|
||||
m_connection_wrapper.connection()->async_find_declaration(GUI::AutocompleteProvider::ProjectLocation { path, line, column });
|
||||
m_connection_wrapper.connection()->async_find_declaration(CodeComprehension::ProjectLocation { path, line, column });
|
||||
}
|
||||
|
||||
void LanguageClient::get_parameters_hint(String const& path, size_t line, size_t column)
|
||||
|
@ -143,7 +143,7 @@ void LanguageClient::get_parameters_hint(String const& path, size_t line, size_t
|
|||
if (!m_connection_wrapper.connection())
|
||||
return;
|
||||
set_active_client();
|
||||
m_connection_wrapper.connection()->async_get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation { path, line, column });
|
||||
m_connection_wrapper.connection()->async_get_parameters_hint(CodeComprehension::ProjectLocation { path, line, column });
|
||||
}
|
||||
|
||||
void LanguageClient::get_tokens_info(String const& filename)
|
||||
|
|
|
@ -46,12 +46,12 @@ public:
|
|||
LanguageClient const* active_client() const { return !m_current_language_client ? nullptr : m_current_language_client.ptr(); }
|
||||
|
||||
protected:
|
||||
virtual void auto_complete_suggestions(Vector<GUI::AutocompleteProvider::Entry> const&) override;
|
||||
virtual void declaration_location(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||
virtual void declarations_in_document(String const&, Vector<GUI::AutocompleteProvider::Declaration> const&) override;
|
||||
virtual void todo_entries_in_document(String const&, Vector<Cpp::Parser::TodoEntry> const&) override;
|
||||
virtual void auto_complete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> const&) override;
|
||||
virtual void declaration_location(CodeComprehension::ProjectLocation const&) override;
|
||||
virtual void declarations_in_document(String const&, Vector<CodeComprehension::Declaration> const&) override;
|
||||
virtual void todo_entries_in_document(String const&, Vector<CodeComprehension::TodoEntry> const&) override;
|
||||
virtual void parameters_hint_result(Vector<String> const&, int index) override;
|
||||
virtual void tokens_info_result(Vector<GUI::AutocompleteProvider::TokenInfo> const&) override;
|
||||
virtual void tokens_info_result(Vector<CodeComprehension::TokenInfo> const&) override;
|
||||
void set_wrapper(ConnectionToServerWrapper& wrapper) { m_wrapper = &wrapper; }
|
||||
|
||||
String m_project_path;
|
||||
|
@ -137,15 +137,15 @@ public:
|
|||
virtual void get_parameters_hint(String const& path, size_t line, size_t column);
|
||||
virtual void get_tokens_info(String const& filename);
|
||||
|
||||
void provide_autocomplete_suggestions(Vector<GUI::AutocompleteProvider::Entry> const&) const;
|
||||
void provide_autocomplete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> const&) const;
|
||||
void declaration_found(String const& file, size_t line, size_t column) const;
|
||||
void parameters_hint_result(Vector<String> const& params, size_t argument_index) const;
|
||||
|
||||
// Callbacks that get called when the result of a language server query is ready
|
||||
Function<void(Vector<GUI::AutocompleteProvider::Entry>)> on_autocomplete_suggestions;
|
||||
Function<void(Vector<CodeComprehension::AutocompleteResultEntry>)> on_autocomplete_suggestions;
|
||||
Function<void(String const&, size_t, size_t)> on_declaration_found;
|
||||
Function<void(Vector<String> const&, size_t)> on_function_parameters_hint_result;
|
||||
Function<void(Vector<GUI::AutocompleteProvider::TokenInfo> const&)> on_tokens_info_result;
|
||||
Function<void(Vector<CodeComprehension::TokenInfo> const&)> on_tokens_info_result;
|
||||
|
||||
private:
|
||||
ConnectionToServerWrapper& m_connection_wrapper;
|
||||
|
|
|
@ -2,7 +2,6 @@ compile_ipc(LanguageServer.ipc LanguageServerEndpoint.h)
|
|||
compile_ipc(LanguageClient.ipc LanguageClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
CodeComprehensionEngine.cpp
|
||||
ConnectionFromClient.cpp
|
||||
FileDB.cpp)
|
||||
set(GENERATED_SOURCES
|
||||
|
@ -10,7 +9,7 @@ set(GENERATED_SOURCES
|
|||
LanguageServerEndpoint.h)
|
||||
|
||||
serenity_lib(LibLanguageServer language_server)
|
||||
target_link_libraries(LibLanguageServer LibC)
|
||||
target_link_libraries(LibLanguageServer LibCodeComprehension LibC)
|
||||
|
||||
add_subdirectory(Cpp)
|
||||
add_subdirectory(Shell)
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "CodeComprehensionEngine.h"
|
||||
|
||||
namespace LanguageServers {
|
||||
|
||||
CodeComprehensionEngine::CodeComprehensionEngine(FileDB const& filedb, bool should_store_all_declarations)
|
||||
: m_filedb(filedb)
|
||||
, m_store_all_declarations(should_store_all_declarations)
|
||||
{
|
||||
}
|
||||
|
||||
void CodeComprehensionEngine::set_declarations_of_document(String const& filename, Vector<GUI::AutocompleteProvider::Declaration>&& declarations)
|
||||
{
|
||||
// Callback may not be configured if we're running tests
|
||||
if (!set_declarations_of_document_callback)
|
||||
return;
|
||||
|
||||
// Optimization - Only notify callback if declarations have changed
|
||||
if (auto previous_declarations = m_all_declarations.find(filename); previous_declarations != m_all_declarations.end()) {
|
||||
if (previous_declarations->value == declarations)
|
||||
return;
|
||||
}
|
||||
if (m_store_all_declarations)
|
||||
m_all_declarations.set(filename, declarations);
|
||||
set_declarations_of_document_callback(filename, move(declarations));
|
||||
}
|
||||
|
||||
void CodeComprehensionEngine::set_todo_entries_of_document(String const& filename, Vector<Cpp::Parser::TodoEntry>&& todo_entries)
|
||||
{
|
||||
// Callback may not be configured if we're running tests
|
||||
if (!set_todo_entries_of_document_callback)
|
||||
return;
|
||||
set_todo_entries_of_document_callback(filename, move(todo_entries));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../AutoCompleteResponse.h"
|
||||
#include "FileDB.h"
|
||||
#include <LibGUI/AutocompleteProvider.h>
|
||||
#include <LibGUI/TextPosition.h>
|
||||
|
||||
namespace LanguageServers {
|
||||
|
||||
class ConnectionFromClient;
|
||||
|
||||
class CodeComprehensionEngine {
|
||||
public:
|
||||
CodeComprehensionEngine(FileDB const& filedb, bool store_all_declarations = false);
|
||||
virtual ~CodeComprehensionEngine() = default;
|
||||
|
||||
virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(String const& file, const GUI::TextPosition& autocomplete_position) = 0;
|
||||
|
||||
// TODO: In the future we can pass the range that was edited and only re-parse what we have to.
|
||||
virtual void on_edit([[maybe_unused]] String const& file) {};
|
||||
virtual void file_opened([[maybe_unused]] String const& file) {};
|
||||
|
||||
virtual Optional<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(String const&, const GUI::TextPosition&) { return {}; }
|
||||
|
||||
struct FunctionParamsHint {
|
||||
Vector<String> params;
|
||||
size_t current_index { 0 };
|
||||
};
|
||||
virtual Optional<FunctionParamsHint> get_function_params_hint(String const&, const GUI::TextPosition&) { return {}; }
|
||||
|
||||
virtual Vector<GUI::AutocompleteProvider::TokenInfo> get_tokens_info(String const&) { return {}; }
|
||||
|
||||
public:
|
||||
Function<void(String const&, Vector<GUI::AutocompleteProvider::Declaration>&&)> set_declarations_of_document_callback;
|
||||
Function<void(String const&, Vector<Cpp::Parser::TodoEntry>&&)> set_todo_entries_of_document_callback;
|
||||
|
||||
protected:
|
||||
FileDB const& filedb() const { return m_filedb; }
|
||||
void set_declarations_of_document(String const&, Vector<GUI::AutocompleteProvider::Declaration>&&);
|
||||
void set_todo_entries_of_document(String const&, Vector<Cpp::Parser::TodoEntry>&&);
|
||||
HashMap<String, Vector<GUI::AutocompleteProvider::Declaration>> const& all_declarations() const { return m_all_declarations; }
|
||||
|
||||
private:
|
||||
HashMap<String, Vector<GUI::AutocompleteProvider::Declaration>> m_all_declarations;
|
||||
FileDB const& m_filedb;
|
||||
bool m_store_all_declarations { false };
|
||||
};
|
||||
}
|
|
@ -66,25 +66,25 @@ void ConnectionFromClient::file_edit_remove_text(String const& filename, i32 sta
|
|||
m_autocomplete_engine->on_edit(filename);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation const& location)
|
||||
void ConnectionFromClient::auto_complete_suggestions(CodeComprehension::ProjectLocation const& location)
|
||||
{
|
||||
dbgln_if(LANGUAGE_SERVER_DEBUG, "AutoCompleteSuggestions for: {} {}:{}", location.file, location.line, location.column);
|
||||
|
||||
auto document = m_filedb.get(location.file);
|
||||
auto document = m_filedb.get_document(location.file);
|
||||
if (!document) {
|
||||
dbgln("file {} has not been opened", location.file);
|
||||
return;
|
||||
}
|
||||
|
||||
GUI::TextPosition autocomplete_position = { (size_t)location.line, (size_t)max(location.column, location.column - 1) };
|
||||
Vector<GUI::AutocompleteProvider::Entry> suggestions = m_autocomplete_engine->get_suggestions(location.file, autocomplete_position);
|
||||
Vector<CodeComprehension::AutocompleteResultEntry> suggestions = m_autocomplete_engine->get_suggestions(location.file, autocomplete_position);
|
||||
async_auto_complete_suggestions(move(suggestions));
|
||||
}
|
||||
|
||||
void ConnectionFromClient::set_file_content(String const& filename, String const& content)
|
||||
{
|
||||
dbgln_if(LANGUAGE_SERVER_DEBUG, "SetFileContent: {}", filename);
|
||||
auto document = m_filedb.get(filename);
|
||||
auto document = m_filedb.get_document(filename);
|
||||
if (!document) {
|
||||
m_filedb.add(filename, content);
|
||||
VERIFY(m_filedb.is_open(filename));
|
||||
|
@ -95,10 +95,10 @@ void ConnectionFromClient::set_file_content(String const& filename, String const
|
|||
m_autocomplete_engine->on_edit(filename);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::find_declaration(GUI::AutocompleteProvider::ProjectLocation const& location)
|
||||
void ConnectionFromClient::find_declaration(CodeComprehension::ProjectLocation const& location)
|
||||
{
|
||||
dbgln_if(LANGUAGE_SERVER_DEBUG, "FindDeclaration: {} {}:{}", location.file, location.line, location.column);
|
||||
auto document = m_filedb.get(location.file);
|
||||
auto document = m_filedb.get_document(location.file);
|
||||
if (!document) {
|
||||
dbgln("file {} has not been opened", location.file);
|
||||
return;
|
||||
|
@ -112,13 +112,13 @@ void ConnectionFromClient::find_declaration(GUI::AutocompleteProvider::ProjectLo
|
|||
}
|
||||
|
||||
dbgln_if(LANGUAGE_SERVER_DEBUG, "declaration location: {} {}:{}", decl_location.value().file, decl_location.value().line, decl_location.value().column);
|
||||
async_declaration_location(GUI::AutocompleteProvider::ProjectLocation { decl_location.value().file, decl_location.value().line, decl_location.value().column });
|
||||
async_declaration_location(CodeComprehension::ProjectLocation { decl_location.value().file, decl_location.value().line, decl_location.value().column });
|
||||
}
|
||||
|
||||
void ConnectionFromClient::get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation const& location)
|
||||
void ConnectionFromClient::get_parameters_hint(CodeComprehension::ProjectLocation const& location)
|
||||
{
|
||||
dbgln_if(LANGUAGE_SERVER_DEBUG, "GetParametersHint: {} {}:{}", location.file, location.line, location.column);
|
||||
auto document = m_filedb.get(location.file);
|
||||
auto document = m_filedb.get_document(location.file);
|
||||
if (!document) {
|
||||
dbgln("file {} has not been opened", location.file);
|
||||
return;
|
||||
|
@ -143,7 +143,7 @@ void ConnectionFromClient::get_parameters_hint(GUI::AutocompleteProvider::Projec
|
|||
void ConnectionFromClient::get_tokens_info(String const& filename)
|
||||
{
|
||||
dbgln_if(LANGUAGE_SERVER_DEBUG, "GetTokenInfo: {}", filename);
|
||||
auto document = m_filedb.get(filename);
|
||||
auto document = m_filedb.get_document(filename);
|
||||
if (!document) {
|
||||
dbgln("file {} has not been opened", filename);
|
||||
return;
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "../AutoCompleteResponse.h"
|
||||
#include "CodeComprehensionEngine.h"
|
||||
#include "FileDB.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibCodeComprehension/CodeComprehensionEngine.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
|
||||
#include <Userland/DevTools/HackStudio/LanguageServers/LanguageClientEndpoint.h>
|
||||
|
@ -32,13 +32,13 @@ protected:
|
|||
virtual void file_edit_insert_text(String const&, String const&, i32, i32) override;
|
||||
virtual void file_edit_remove_text(String const&, i32, i32, i32, i32) override;
|
||||
virtual void set_file_content(String const&, String const&) override;
|
||||
virtual void auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||
virtual void find_declaration(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||
virtual void get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation const&) override;
|
||||
virtual void auto_complete_suggestions(CodeComprehension::ProjectLocation const&) override;
|
||||
virtual void find_declaration(CodeComprehension::ProjectLocation const&) override;
|
||||
virtual void get_parameters_hint(CodeComprehension::ProjectLocation const&) override;
|
||||
virtual void get_tokens_info(String const&) override;
|
||||
|
||||
FileDB m_filedb;
|
||||
OwnPtr<CodeComprehensionEngine> m_autocomplete_engine;
|
||||
OwnPtr<CodeComprehension::CodeComprehensionEngine> m_autocomplete_engine;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ serenity_component(
|
|||
)
|
||||
|
||||
set(SOURCES
|
||||
CppComprehensionEngine.cpp
|
||||
Tests.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
|
@ -17,4 +15,4 @@ serenity_bin(CppLanguageServer)
|
|||
|
||||
# We link with LibGUI because we use GUI::TextDocument to update
|
||||
# the content of files according to the edit actions we receive over IPC.
|
||||
target_link_libraries(CppLanguageServer LibIPC LibCpp LibGUI LibLanguageServer LibMain)
|
||||
target_link_libraries(CppLanguageServer LibIPC LibCpp LibGUI LibLanguageServer LibCppComprehension LibMain)
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "CppComprehensionEngine.h"
|
||||
#include <DevTools/HackStudio/LanguageServers/ConnectionFromClient.h>
|
||||
#include <LibCodeComprehension/Cpp/CppComprehensionEngine.h>
|
||||
|
||||
namespace LanguageServers::Cpp {
|
||||
|
||||
|
@ -18,11 +18,11 @@ private:
|
|||
ConnectionFromClient(NonnullOwnPtr<Core::Stream::LocalSocket> socket)
|
||||
: LanguageServers::ConnectionFromClient(move(socket))
|
||||
{
|
||||
m_autocomplete_engine = make<CppComprehensionEngine>(m_filedb);
|
||||
m_autocomplete_engine->set_declarations_of_document_callback = [this](String const& filename, Vector<GUI::AutocompleteProvider::Declaration>&& declarations) {
|
||||
m_autocomplete_engine = adopt_own(*new CodeComprehension::Cpp::CppComprehensionEngine(m_filedb));
|
||||
m_autocomplete_engine->set_declarations_of_document_callback = [this](String const& filename, Vector<CodeComprehension::Declaration>&& declarations) {
|
||||
async_declarations_in_document(filename, move(declarations));
|
||||
};
|
||||
m_autocomplete_engine->set_todo_entries_of_document_callback = [this](String const& filename, Vector<Cpp::Parser::TodoEntry>&& todo_entries) {
|
||||
m_autocomplete_engine->set_todo_entries_of_document_callback = [this](String const& filename, Vector<CodeComprehension::TodoEntry>&& todo_entries) {
|
||||
async_todo_entries_in_document(filename, move(todo_entries));
|
||||
};
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,204 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <DevTools/HackStudio/AutoCompleteResponse.h>
|
||||
#include <DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h>
|
||||
#include <DevTools/HackStudio/LanguageServers/FileDB.h>
|
||||
#include <LibCpp/AST.h>
|
||||
#include <LibCpp/Parser.h>
|
||||
#include <LibCpp/Preprocessor.h>
|
||||
#include <LibGUI/TextPosition.h>
|
||||
|
||||
namespace LanguageServers::Cpp {
|
||||
|
||||
using namespace ::Cpp;
|
||||
|
||||
class CppComprehensionEngine : public CodeComprehensionEngine {
|
||||
public:
|
||||
CppComprehensionEngine(FileDB const& filedb);
|
||||
|
||||
virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(String const& file, const GUI::TextPosition& autocomplete_position) override;
|
||||
virtual void on_edit(String const& file) override;
|
||||
virtual void file_opened([[maybe_unused]] String const& file) override;
|
||||
virtual Optional<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(String const& filename, const GUI::TextPosition& identifier_position) override;
|
||||
virtual Optional<FunctionParamsHint> get_function_params_hint(String const&, const GUI::TextPosition&) override;
|
||||
virtual Vector<GUI::AutocompleteProvider::TokenInfo> get_tokens_info(String const& filename) override;
|
||||
|
||||
private:
|
||||
struct SymbolName {
|
||||
StringView name;
|
||||
Vector<StringView> scope;
|
||||
|
||||
static SymbolName create(StringView, Vector<StringView>&&);
|
||||
static SymbolName create(StringView);
|
||||
String scope_as_string() const;
|
||||
String to_string() const;
|
||||
|
||||
bool operator==(SymbolName const&) const = default;
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
SymbolName name;
|
||||
NonnullRefPtr<Declaration> declaration;
|
||||
|
||||
// Local symbols are symbols that should not appear in a global symbol search.
|
||||
// For example, a variable that is declared inside a function will have is_local = true.
|
||||
bool is_local { false };
|
||||
|
||||
enum class IsLocal {
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
static Symbol create(StringView name, Vector<StringView> const& scope, NonnullRefPtr<Declaration>, IsLocal is_local);
|
||||
};
|
||||
|
||||
friend Traits<SymbolName>;
|
||||
|
||||
struct DocumentData {
|
||||
String const& filename() const { return m_filename; }
|
||||
String const& text() const { return m_text; }
|
||||
Preprocessor const& preprocessor() const
|
||||
{
|
||||
VERIFY(m_preprocessor);
|
||||
return *m_preprocessor;
|
||||
}
|
||||
Preprocessor& preprocessor()
|
||||
{
|
||||
VERIFY(m_preprocessor);
|
||||
return *m_preprocessor;
|
||||
}
|
||||
Parser const& parser() const
|
||||
{
|
||||
VERIFY(m_parser);
|
||||
return *m_parser;
|
||||
}
|
||||
Parser& parser()
|
||||
{
|
||||
VERIFY(m_parser);
|
||||
return *m_parser;
|
||||
}
|
||||
|
||||
String m_filename;
|
||||
String m_text;
|
||||
OwnPtr<Preprocessor> m_preprocessor;
|
||||
OwnPtr<Parser> m_parser;
|
||||
|
||||
HashMap<SymbolName, Symbol> m_symbols;
|
||||
HashTable<String> m_available_headers;
|
||||
};
|
||||
|
||||
Vector<GUI::AutocompleteProvider::Entry> autocomplete_property(DocumentData const&, MemberExpression const&, const String partial_text) const;
|
||||
Vector<GUI::AutocompleteProvider::Entry> autocomplete_name(DocumentData const&, ASTNode const&, String const& partial_text) const;
|
||||
String type_of(DocumentData const&, Expression const&) const;
|
||||
String type_of_property(DocumentData const&, Identifier const&) const;
|
||||
String type_of_variable(Identifier const&) const;
|
||||
bool is_property(ASTNode const&) const;
|
||||
RefPtr<Declaration> find_declaration_of(DocumentData const&, ASTNode const&) const;
|
||||
RefPtr<Declaration> find_declaration_of(DocumentData const&, SymbolName const&) const;
|
||||
RefPtr<Declaration> find_declaration_of(DocumentData const&, const GUI::TextPosition& identifier_position);
|
||||
|
||||
enum class RecurseIntoScopes {
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
|
||||
Vector<Symbol> properties_of_type(DocumentData const& document, String const& type) const;
|
||||
Vector<Symbol> get_child_symbols(ASTNode const&) const;
|
||||
Vector<Symbol> get_child_symbols(ASTNode const&, Vector<StringView> const& scope, Symbol::IsLocal) const;
|
||||
|
||||
DocumentData const* get_document_data(String const& file) const;
|
||||
DocumentData const* get_or_create_document_data(String const& file);
|
||||
void set_document_data(String const& file, OwnPtr<DocumentData>&& data);
|
||||
|
||||
OwnPtr<DocumentData> create_document_data_for(String const& file);
|
||||
String document_path_from_include_path(StringView include_path) const;
|
||||
void update_declared_symbols(DocumentData&);
|
||||
void update_todo_entries(DocumentData&);
|
||||
GUI::AutocompleteProvider::DeclarationType type_of_declaration(Declaration const&);
|
||||
Vector<StringView> scope_of_node(ASTNode const&) const;
|
||||
Vector<StringView> scope_of_reference_to_symbol(ASTNode const&) const;
|
||||
|
||||
Optional<GUI::AutocompleteProvider::ProjectLocation> find_preprocessor_definition(DocumentData const&, const GUI::TextPosition&);
|
||||
Optional<Cpp::Preprocessor::Substitution> find_preprocessor_substitution(DocumentData const&, Cpp::Position const&);
|
||||
|
||||
OwnPtr<DocumentData> create_document_data(String&& text, String const& filename);
|
||||
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_property(DocumentData const&, ASTNode const&, Optional<Token> containing_token) const;
|
||||
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_name(DocumentData const&, ASTNode const&, Optional<Token> containing_token) const;
|
||||
Optional<Vector<GUI::AutocompleteProvider::Entry>> try_autocomplete_include(DocumentData const&, Token include_path_token, Cpp::Position const& cursor_position) const;
|
||||
static bool is_symbol_available(Symbol const&, Vector<StringView> const& current_scope, Vector<StringView> const& reference_scope);
|
||||
Optional<FunctionParamsHint> get_function_params_hint(DocumentData const&, FunctionCall&, size_t argument_index);
|
||||
|
||||
template<typename Func>
|
||||
void for_each_available_symbol(DocumentData const&, Func) const;
|
||||
|
||||
template<typename Func>
|
||||
void for_each_included_document_recursive(DocumentData const&, Func) const;
|
||||
|
||||
GUI::AutocompleteProvider::TokenInfo::SemanticType get_token_semantic_type(DocumentData const&, Token const&);
|
||||
GUI::AutocompleteProvider::TokenInfo::SemanticType get_semantic_type_for_identifier(DocumentData const&, Position);
|
||||
|
||||
HashMap<String, OwnPtr<DocumentData>> m_documents;
|
||||
|
||||
// A document's path will be in this set if we're currently processing it.
|
||||
// A document is added to this set when we start processing it (e.g because it was #included) and removed when we're done.
|
||||
// We use this to prevent circular #includes from looping indefinitely.
|
||||
HashTable<String> m_unfinished_documents;
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
void CppComprehensionEngine::for_each_available_symbol(DocumentData const& document, Func func) const
|
||||
{
|
||||
for (auto& item : document.m_symbols) {
|
||||
auto decision = func(item.value);
|
||||
if (decision == IterationDecision::Break)
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_included_document_recursive(document, [&](DocumentData const& document) {
|
||||
for (auto& item : document.m_symbols) {
|
||||
auto decision = func(item.value);
|
||||
if (decision == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void CppComprehensionEngine::for_each_included_document_recursive(DocumentData const& document, Func func) const
|
||||
{
|
||||
for (auto& included_path : document.m_available_headers) {
|
||||
auto* included_document = get_document_data(included_path);
|
||||
if (!included_document)
|
||||
continue;
|
||||
auto decision = func(*included_document);
|
||||
if (decision == IterationDecision::Break)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<>
|
||||
struct Traits<LanguageServers::Cpp::CppComprehensionEngine::SymbolName> : public GenericTraits<LanguageServers::Cpp::CppComprehensionEngine::SymbolName> {
|
||||
static unsigned hash(LanguageServers::Cpp::CppComprehensionEngine::SymbolName const& key)
|
||||
{
|
||||
unsigned hash = 0;
|
||||
hash = pair_int_hash(hash, string_hash(key.name.characters_without_null_termination(), key.name.length()));
|
||||
for (auto& scope_part : key.scope) {
|
||||
hash = pair_int_hash(hash, string_hash(scope_part.characters_without_null_termination(), scope_part.length()));
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Tests.h"
|
||||
#include "../FileDB.h"
|
||||
#include "CppComprehensionEngine.h"
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibCore/File.h>
|
||||
|
||||
using namespace LanguageServers;
|
||||
using namespace LanguageServers::Cpp;
|
||||
|
||||
static bool s_some_test_failed = false;
|
||||
|
||||
#define I_TEST(name) \
|
||||
{ \
|
||||
printf("Testing " #name "... "); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define PASS \
|
||||
do { \
|
||||
printf("PASS\n"); \
|
||||
fflush(stdout); \
|
||||
return; \
|
||||
} while (0)
|
||||
|
||||
#define FAIL(reason) \
|
||||
do { \
|
||||
printf("FAIL: " #reason "\n"); \
|
||||
s_some_test_failed = true; \
|
||||
return; \
|
||||
} while (0)
|
||||
|
||||
constexpr char TESTS_ROOT_DIR[] = "/home/anon/Tests/cpp-tests/comprehension";
|
||||
|
||||
static void test_complete_local_args();
|
||||
static void test_complete_local_vars();
|
||||
static void test_complete_type();
|
||||
static void test_find_variable_definition();
|
||||
static void test_complete_includes();
|
||||
static void test_parameters_hint();
|
||||
|
||||
int run_tests()
|
||||
{
|
||||
test_complete_local_args();
|
||||
test_complete_local_vars();
|
||||
test_complete_type();
|
||||
test_find_variable_definition();
|
||||
test_complete_includes();
|
||||
test_parameters_hint();
|
||||
return s_some_test_failed ? 1 : 0;
|
||||
}
|
||||
|
||||
static void add_file(FileDB& filedb, String const& name)
|
||||
{
|
||||
auto file = Core::File::open(LexicalPath::join(TESTS_ROOT_DIR, name).string(), Core::OpenMode::ReadOnly);
|
||||
VERIFY(!file.is_error());
|
||||
filedb.add(name, file.value()->fd());
|
||||
}
|
||||
|
||||
void test_complete_local_args()
|
||||
{
|
||||
I_TEST(Complete Local Args)
|
||||
FileDB filedb;
|
||||
add_file(filedb, "complete_local_args.cpp");
|
||||
CppComprehensionEngine engine(filedb);
|
||||
auto suggestions = engine.get_suggestions("complete_local_args.cpp", { 2, 6 });
|
||||
if (suggestions.size() != 2)
|
||||
FAIL(bad size);
|
||||
|
||||
if (suggestions[0].completion == "argc" && suggestions[1].completion == "argv")
|
||||
PASS;
|
||||
|
||||
FAIL("wrong results");
|
||||
}
|
||||
|
||||
void test_complete_local_vars()
|
||||
{
|
||||
I_TEST(Complete Local Vars)
|
||||
FileDB filedb;
|
||||
add_file(filedb, "complete_local_vars.cpp");
|
||||
CppComprehensionEngine autocomplete(filedb);
|
||||
auto suggestions = autocomplete.get_suggestions("complete_local_vars.cpp", { 3, 7 });
|
||||
if (suggestions.size() != 1)
|
||||
FAIL(bad size);
|
||||
|
||||
if (suggestions[0].completion == "myvar1")
|
||||
PASS;
|
||||
|
||||
FAIL("wrong results");
|
||||
}
|
||||
|
||||
void test_complete_type()
|
||||
{
|
||||
I_TEST(Complete Type)
|
||||
FileDB filedb;
|
||||
add_file(filedb, "complete_type.cpp");
|
||||
CppComprehensionEngine autocomplete(filedb);
|
||||
auto suggestions = autocomplete.get_suggestions("complete_type.cpp", { 5, 7 });
|
||||
if (suggestions.size() != 1)
|
||||
FAIL(bad size);
|
||||
|
||||
if (suggestions[0].completion == "MyStruct")
|
||||
PASS;
|
||||
|
||||
FAIL("wrong results");
|
||||
}
|
||||
|
||||
void test_find_variable_definition()
|
||||
{
|
||||
I_TEST(Find Variable Declaration)
|
||||
FileDB filedb;
|
||||
add_file(filedb, "find_variable_declaration.cpp");
|
||||
CppComprehensionEngine engine(filedb);
|
||||
auto position = engine.find_declaration_of("find_variable_declaration.cpp", { 2, 5 });
|
||||
if (!position.has_value())
|
||||
FAIL("declaration not found");
|
||||
|
||||
if (position.value().file == "find_variable_declaration.cpp" && position.value().line == 0 && position.value().column >= 19)
|
||||
PASS;
|
||||
FAIL("wrong declaration location");
|
||||
}
|
||||
|
||||
void test_complete_includes()
|
||||
{
|
||||
I_TEST(Complete Type)
|
||||
FileDB filedb;
|
||||
filedb.set_project_root(TESTS_ROOT_DIR);
|
||||
add_file(filedb, "complete_includes.cpp");
|
||||
add_file(filedb, "sample_header.h");
|
||||
CppComprehensionEngine autocomplete(filedb);
|
||||
|
||||
auto suggestions = autocomplete.get_suggestions("complete_includes.cpp", { 0, 22 });
|
||||
if (suggestions.size() != 1)
|
||||
FAIL(project include - bad size);
|
||||
|
||||
if (suggestions[0].completion != "\"sample_header.h\"")
|
||||
FAIL("project include - wrong results");
|
||||
|
||||
suggestions = autocomplete.get_suggestions("complete_includes.cpp", { 1, 18 });
|
||||
if (suggestions.size() != 1)
|
||||
FAIL(global include - bad size);
|
||||
|
||||
if (suggestions[0].completion != "<sys/cdefs.h>")
|
||||
FAIL("global include - wrong results");
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
void test_parameters_hint()
|
||||
{
|
||||
I_TEST(Function Parameters hint)
|
||||
FileDB filedb;
|
||||
filedb.set_project_root(TESTS_ROOT_DIR);
|
||||
add_file(filedb, "parameters_hint1.cpp");
|
||||
CppComprehensionEngine engine(filedb);
|
||||
|
||||
auto result = engine.get_function_params_hint("parameters_hint1.cpp", { 4, 9 });
|
||||
if (!result.has_value())
|
||||
FAIL("failed to get parameters hint (1)");
|
||||
if (result->params != Vector<String> { "int x", "char y" } || result->current_index != 0)
|
||||
FAIL("bad result (1)");
|
||||
|
||||
result = engine.get_function_params_hint("parameters_hint1.cpp", { 5, 15 });
|
||||
if (!result.has_value())
|
||||
FAIL("failed to get parameters hint (2)");
|
||||
if (result->params != Vector<String> { "int x", "char y" } || result->current_index != 1)
|
||||
FAIL("bad result (2)");
|
||||
|
||||
result = engine.get_function_params_hint("parameters_hint1.cpp", { 6, 8 });
|
||||
if (!result.has_value())
|
||||
FAIL("failed to get parameters hint (3)");
|
||||
if (result->params != Vector<String> { "int x", "char y" } || result->current_index != 0)
|
||||
FAIL("bad result (3)");
|
||||
|
||||
PASS;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int run_tests();
|
|
@ -1,5 +0,0 @@
|
|||
#include "sample_heade
|
||||
#include <sys/cdef
|
||||
|
||||
void foo() {}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
ar
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
int myvar1 = 3;
|
||||
myv
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
struct MyStruct {
|
||||
int x;
|
||||
};
|
||||
void foo()
|
||||
{
|
||||
MyS
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
argv = nullptr;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
void foo(int x, char y);
|
||||
|
||||
void bar()
|
||||
{
|
||||
foo();
|
||||
foo(123, 'b');
|
||||
foo(
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
int bar();
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include "ConnectionFromClient.h"
|
||||
#include "Tests.h"
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
|
@ -13,23 +12,7 @@
|
|||
#include <LibIPC/SingleServer.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
static ErrorOr<int> mode_server();
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
bool tests = false;
|
||||
|
||||
Core::ArgsParser parser;
|
||||
parser.add_option(tests, "Run tests", "tests", 't');
|
||||
parser.parse(arguments);
|
||||
|
||||
if (tests)
|
||||
return run_tests();
|
||||
|
||||
return mode_server();
|
||||
}
|
||||
|
||||
ErrorOr<int> mode_server()
|
||||
ErrorOr<int> serenity_main(Main::Arguments)
|
||||
{
|
||||
Core::EventLoop event_loop;
|
||||
TRY(Core::System::pledge("stdio unix recvfd rpath"));
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace LanguageServers {
|
||||
|
||||
RefPtr<const GUI::TextDocument> FileDB::get(String const& filename) const
|
||||
RefPtr<const GUI::TextDocument> FileDB::get_document(String const& filename) const
|
||||
{
|
||||
auto absolute_path = to_absolute_path(filename);
|
||||
auto document_optional = m_open_files.get(absolute_path);
|
||||
|
@ -22,29 +22,25 @@ RefPtr<const GUI::TextDocument> FileDB::get(String const& filename) const
|
|||
return *document_optional.value();
|
||||
}
|
||||
|
||||
RefPtr<GUI::TextDocument> FileDB::get(String const& filename)
|
||||
RefPtr<GUI::TextDocument> FileDB::get_document(String const& filename)
|
||||
{
|
||||
auto document = reinterpret_cast<FileDB const*>(this)->get(filename);
|
||||
auto document = reinterpret_cast<FileDB const*>(this)->get_document(filename);
|
||||
if (document.is_null())
|
||||
return nullptr;
|
||||
return adopt_ref(*const_cast<GUI::TextDocument*>(document.leak_ref()));
|
||||
}
|
||||
|
||||
RefPtr<const GUI::TextDocument> FileDB::get_or_create_from_filesystem(String const& filename) const
|
||||
Optional<String> FileDB::get_or_read_from_filesystem(StringView filename) const
|
||||
{
|
||||
auto absolute_path = to_absolute_path(filename);
|
||||
auto document = get(absolute_path);
|
||||
auto document = get_document(absolute_path);
|
||||
if (document)
|
||||
return document;
|
||||
return create_from_filesystem(absolute_path);
|
||||
}
|
||||
return document->text();
|
||||
|
||||
RefPtr<GUI::TextDocument> FileDB::get_or_create_from_filesystem(String const& filename)
|
||||
{
|
||||
auto document = reinterpret_cast<FileDB const*>(this)->get_or_create_from_filesystem(filename);
|
||||
if (document.is_null())
|
||||
return nullptr;
|
||||
return adopt_ref(*const_cast<GUI::TextDocument*>(document.leak_ref()));
|
||||
document = create_from_filesystem(absolute_path);
|
||||
if (document)
|
||||
return document->text();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool FileDB::is_open(String const& filename) const
|
||||
|
@ -123,7 +119,7 @@ RefPtr<GUI::TextDocument> FileDB::create_from_file(Core::File& file) const
|
|||
void FileDB::on_file_edit_insert_text(String const& filename, String const& inserted_text, size_t start_line, size_t start_column)
|
||||
{
|
||||
VERIFY(is_open(filename));
|
||||
auto document = get(filename);
|
||||
auto document = get_document(filename);
|
||||
VERIFY(document);
|
||||
GUI::TextPosition start_position { start_line, start_column };
|
||||
document->insert_at(start_position, inserted_text, &s_default_document_client);
|
||||
|
@ -136,7 +132,7 @@ void FileDB::on_file_edit_remove_text(String const& filename, size_t start_line,
|
|||
// TODO: If file is not open - need to get its contents
|
||||
// Otherwise- somehow verify that respawned language server is synced with all file contents
|
||||
VERIFY(is_open(filename));
|
||||
auto document = get(filename);
|
||||
auto document = get_document(filename);
|
||||
VERIFY(document);
|
||||
GUI::TextPosition start_position { start_line, start_column };
|
||||
GUI::TextRange range {
|
||||
|
|
|
@ -9,22 +9,22 @@
|
|||
#include <AK/HashMap.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCodeComprehension/FileDB.h>
|
||||
#include <LibGUI/TextDocument.h>
|
||||
|
||||
namespace LanguageServers {
|
||||
|
||||
class FileDB final {
|
||||
class FileDB final : public CodeComprehension::FileDB {
|
||||
public:
|
||||
RefPtr<const GUI::TextDocument> get(String const& filename) const;
|
||||
RefPtr<GUI::TextDocument> get(String const& filename);
|
||||
RefPtr<const GUI::TextDocument> get_or_create_from_filesystem(String const& filename) const;
|
||||
RefPtr<GUI::TextDocument> get_or_create_from_filesystem(String const& filename);
|
||||
FileDB() = default;
|
||||
virtual Optional<String> get_or_read_from_filesystem(StringView filename) const override;
|
||||
|
||||
RefPtr<const GUI::TextDocument> get_document(String const& filename) const;
|
||||
RefPtr<GUI::TextDocument> get_document(String const& filename);
|
||||
|
||||
bool add(String const& filename, int fd);
|
||||
bool add(String const& filename, String const& content);
|
||||
|
||||
void set_project_root(String const& root_path) { m_project_root = root_path; }
|
||||
String const& project_root() const { return m_project_root; }
|
||||
|
||||
void on_file_edit_insert_text(String const& filename, String const& inserted_text, size_t start_line, size_t start_column);
|
||||
void on_file_edit_remove_text(String const& filename, size_t start_line, size_t start_column, size_t end_line, size_t end_column);
|
||||
String to_absolute_path(String const& filename) const;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
endpoint LanguageClient
|
||||
{
|
||||
auto_complete_suggestions(Vector<GUI::AutocompleteProvider::Entry> suggestions) =|
|
||||
declaration_location(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
declarations_in_document(String filename, Vector<GUI::AutocompleteProvider::Declaration> declarations) =|
|
||||
todo_entries_in_document(String filename, Vector<Cpp::Parser::TodoEntry> todo_entries) =|
|
||||
auto_complete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> suggestions) =|
|
||||
declaration_location(CodeComprehension::ProjectLocation location) =|
|
||||
declarations_in_document(String filename, Vector<CodeComprehension::Declaration> declarations) =|
|
||||
todo_entries_in_document(String filename, Vector<CodeComprehension::TodoEntry> todo_entries) =|
|
||||
parameters_hint_result(Vector<String> params, int current_index) =|
|
||||
tokens_info_result(Vector<GUI::AutocompleteProvider::TokenInfo> tokens_info) =|
|
||||
tokens_info_result(Vector<CodeComprehension::TokenInfo> tokens_info) =|
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ endpoint LanguageServer
|
|||
file_edit_remove_text(String filename, i32 start_line, i32 start_column, i32 end_line, i32 end_column) =|
|
||||
set_file_content(String filename, String content) =|
|
||||
|
||||
auto_complete_suggestions(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
find_declaration(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
get_parameters_hint(GUI::AutocompleteProvider::ProjectLocation location) =|
|
||||
auto_complete_suggestions(CodeComprehension::ProjectLocation location) =|
|
||||
find_declaration(CodeComprehension::ProjectLocation location) =|
|
||||
get_parameters_hint(CodeComprehension::ProjectLocation location) =|
|
||||
get_tokens_info(String filename) =|
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ serenity_component(
|
|||
)
|
||||
|
||||
set(SOURCES
|
||||
ShellComprehensionEngine.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
|
@ -16,4 +15,4 @@ serenity_bin(ShellLanguageServer)
|
|||
|
||||
# We link with LibGUI because we use GUI::TextDocument to update
|
||||
# the content of files according to the edit actions we receive over IPC.
|
||||
target_link_libraries(ShellLanguageServer LibIPC LibShell LibGUI LibLanguageServer LibMain)
|
||||
target_link_libraries(ShellLanguageServer LibIPC LibShell LibGUI LibLanguageServer LibShellComprehension LibMain)
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ShellComprehensionEngine.h"
|
||||
#include <DevTools/HackStudio/LanguageServers/ConnectionFromClient.h>
|
||||
#include <LibCodeComprehension/Shell/ShellComprehensionEngine.h>
|
||||
#include <LibCpp/Parser.h>
|
||||
|
||||
namespace LanguageServers::Shell {
|
||||
|
@ -19,11 +19,11 @@ private:
|
|||
ConnectionFromClient(NonnullOwnPtr<Core::Stream::LocalSocket> socket)
|
||||
: LanguageServers::ConnectionFromClient(move(socket))
|
||||
{
|
||||
m_autocomplete_engine = make<ShellComprehensionEngine>(m_filedb);
|
||||
m_autocomplete_engine->set_declarations_of_document_callback = [this](String const& filename, Vector<GUI::AutocompleteProvider::Declaration>&& declarations) {
|
||||
m_autocomplete_engine = make<CodeComprehension::Shell::ShellComprehensionEngine>(m_filedb);
|
||||
m_autocomplete_engine->set_declarations_of_document_callback = [this](String const& filename, Vector<CodeComprehension::Declaration>&& declarations) {
|
||||
async_declarations_in_document(filename, move(declarations));
|
||||
};
|
||||
m_autocomplete_engine->set_todo_entries_of_document_callback = [this](String const& filename, Vector<Cpp::Parser::TodoEntry>&& todo_entries) {
|
||||
m_autocomplete_engine->set_todo_entries_of_document_callback = [this](String const& filename, Vector<CodeComprehension::TodoEntry>&& todo_entries) {
|
||||
async_todo_entries_in_document(filename, move(todo_entries));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "ShellComprehensionEngine.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <LibRegex/Regex.h>
|
||||
#include <Userland/DevTools/HackStudio/LanguageServers/ConnectionFromClient.h>
|
||||
|
||||
namespace LanguageServers::Shell {
|
||||
|
||||
RefPtr<::Shell::Shell> ShellComprehensionEngine::s_shell {};
|
||||
|
||||
ShellComprehensionEngine::ShellComprehensionEngine(FileDB const& filedb)
|
||||
: CodeComprehensionEngine(filedb, true)
|
||||
{
|
||||
}
|
||||
|
||||
ShellComprehensionEngine::DocumentData const& ShellComprehensionEngine::get_or_create_document_data(String const& file)
|
||||
{
|
||||
auto absolute_path = filedb().to_absolute_path(file);
|
||||
if (!m_documents.contains(absolute_path)) {
|
||||
set_document_data(absolute_path, create_document_data_for(absolute_path));
|
||||
}
|
||||
return get_document_data(absolute_path);
|
||||
}
|
||||
|
||||
ShellComprehensionEngine::DocumentData const& ShellComprehensionEngine::get_document_data(String const& file) const
|
||||
{
|
||||
auto absolute_path = filedb().to_absolute_path(file);
|
||||
auto document_data = m_documents.get(absolute_path);
|
||||
VERIFY(document_data.has_value());
|
||||
return *document_data.value();
|
||||
}
|
||||
|
||||
OwnPtr<ShellComprehensionEngine::DocumentData> ShellComprehensionEngine::create_document_data_for(String const& file)
|
||||
{
|
||||
auto document = filedb().get(file);
|
||||
if (!document)
|
||||
return {};
|
||||
auto content = document->text();
|
||||
auto document_data = make<DocumentData>(document->text(), file);
|
||||
for (auto& path : document_data->sourced_paths())
|
||||
get_or_create_document_data(path);
|
||||
|
||||
update_declared_symbols(*document_data);
|
||||
return document_data;
|
||||
}
|
||||
|
||||
void ShellComprehensionEngine::set_document_data(String const& file, OwnPtr<DocumentData>&& data)
|
||||
{
|
||||
m_documents.set(filedb().to_absolute_path(file), move(data));
|
||||
}
|
||||
|
||||
ShellComprehensionEngine::DocumentData::DocumentData(String&& _text, String _filename)
|
||||
: filename(move(_filename))
|
||||
, text(move(_text))
|
||||
, node(parse())
|
||||
{
|
||||
}
|
||||
|
||||
Vector<String> const& ShellComprehensionEngine::DocumentData::sourced_paths() const
|
||||
{
|
||||
if (all_sourced_paths.has_value())
|
||||
return all_sourced_paths.value();
|
||||
|
||||
struct : public ::Shell::AST::NodeVisitor {
|
||||
void visit(const ::Shell::AST::CastToCommand* node) override
|
||||
{
|
||||
auto& inner = node->inner();
|
||||
if (inner->is_list()) {
|
||||
if (auto* list = dynamic_cast<const ::Shell::AST::ListConcatenate*>(inner.ptr())) {
|
||||
auto& entries = list->list();
|
||||
if (entries.size() == 2 && entries.first()->is_bareword() && static_ptr_cast<::Shell::AST::BarewordLiteral>(entries.first())->text() == "source") {
|
||||
auto& filename = entries[1];
|
||||
if (filename->would_execute())
|
||||
return;
|
||||
auto name_list = const_cast<::Shell::AST::Node*>(filename.ptr())->run(nullptr)->resolve_as_list(nullptr);
|
||||
StringBuilder builder;
|
||||
builder.join(" ", name_list);
|
||||
sourced_files.set(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
::Shell::AST::NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
HashTable<String> sourced_files;
|
||||
} visitor;
|
||||
|
||||
node->visit(visitor);
|
||||
|
||||
Vector<String> sourced_paths;
|
||||
for (auto& entry : visitor.sourced_files)
|
||||
sourced_paths.append(move(entry));
|
||||
|
||||
all_sourced_paths = move(sourced_paths);
|
||||
return all_sourced_paths.value();
|
||||
}
|
||||
|
||||
NonnullRefPtr<::Shell::AST::Node> ShellComprehensionEngine::DocumentData::parse() const
|
||||
{
|
||||
::Shell::Parser parser { text };
|
||||
if (auto node = parser.parse())
|
||||
return node.release_nonnull();
|
||||
|
||||
return ::Shell::AST::make_ref_counted<::Shell::AST::SyntaxError>(::Shell::AST::Position {}, "Unable to parse file");
|
||||
}
|
||||
|
||||
size_t ShellComprehensionEngine::resolve(ShellComprehensionEngine::DocumentData const& document, const GUI::TextPosition& position)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
if (position.line() > 0) {
|
||||
auto first = true;
|
||||
size_t line = 0;
|
||||
for (auto& line_view : document.text.split_limit('\n', position.line() + 1, true)) {
|
||||
if (line == position.line())
|
||||
break;
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
++offset; // For the newline.
|
||||
offset += line_view.length();
|
||||
++line;
|
||||
}
|
||||
}
|
||||
|
||||
offset += position.column() + 1;
|
||||
return offset;
|
||||
}
|
||||
|
||||
Vector<GUI::AutocompleteProvider::Entry> ShellComprehensionEngine::get_suggestions(String const& file, const GUI::TextPosition& position)
|
||||
{
|
||||
dbgln_if(SH_LANGUAGE_SERVER_DEBUG, "ShellComprehensionEngine position {}:{}", position.line(), position.column());
|
||||
|
||||
auto const& document = get_or_create_document_data(file);
|
||||
size_t offset_in_file = resolve(document, position);
|
||||
|
||||
::Shell::AST::HitTestResult hit_test = document.node->hit_test_position(offset_in_file);
|
||||
if (!hit_test.matching_node) {
|
||||
dbgln_if(SH_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", position.line(), position.column());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto completions = const_cast<::Shell::AST::Node*>(document.node.ptr())->complete_for_editor(shell(), offset_in_file, hit_test);
|
||||
Vector<GUI::AutocompleteProvider::Entry> entries;
|
||||
for (auto& completion : completions)
|
||||
entries.append({ completion.text_string, completion.input_offset });
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
void ShellComprehensionEngine::on_edit(String const& file)
|
||||
{
|
||||
set_document_data(file, create_document_data_for(file));
|
||||
}
|
||||
|
||||
void ShellComprehensionEngine::file_opened([[maybe_unused]] String const& file)
|
||||
{
|
||||
set_document_data(file, create_document_data_for(file));
|
||||
}
|
||||
|
||||
Optional<GUI::AutocompleteProvider::ProjectLocation> ShellComprehensionEngine::find_declaration_of(String const& filename, const GUI::TextPosition& identifier_position)
|
||||
{
|
||||
dbgln_if(SH_LANGUAGE_SERVER_DEBUG, "find_declaration_of({}, {}:{})", filename, identifier_position.line(), identifier_position.column());
|
||||
auto const& document = get_or_create_document_data(filename);
|
||||
auto position = resolve(document, identifier_position);
|
||||
auto result = document.node->hit_test_position(position);
|
||||
if (!result.matching_node) {
|
||||
dbgln_if(SH_LANGUAGE_SERVER_DEBUG, "no node at position {}:{}", identifier_position.line(), identifier_position.column());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!result.matching_node->is_bareword()) {
|
||||
dbgln_if(SH_LANGUAGE_SERVER_DEBUG, "no bareword at position {}:{}", identifier_position.line(), identifier_position.column());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto name = static_ptr_cast<::Shell::AST::BarewordLiteral>(result.matching_node)->text();
|
||||
auto& declarations = all_declarations();
|
||||
for (auto& entry : declarations) {
|
||||
for (auto& declaration : entry.value) {
|
||||
if (declaration.name == name)
|
||||
return declaration.position;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ShellComprehensionEngine::update_declared_symbols(DocumentData const& document)
|
||||
{
|
||||
struct Visitor : public ::Shell::AST::NodeVisitor {
|
||||
explicit Visitor(String const& filename)
|
||||
: filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
void visit(const ::Shell::AST::VariableDeclarations* node) override
|
||||
{
|
||||
for (auto& entry : node->variables()) {
|
||||
auto literal = entry.name->leftmost_trivial_literal();
|
||||
if (!literal)
|
||||
continue;
|
||||
|
||||
String name;
|
||||
if (literal->is_bareword())
|
||||
name = static_ptr_cast<::Shell::AST::BarewordLiteral>(literal)->text();
|
||||
|
||||
if (!name.is_empty()) {
|
||||
dbgln("Found variable {}", name);
|
||||
declarations.append({ move(name), { filename, entry.name->position().start_line.line_number, entry.name->position().start_line.line_column }, GUI::AutocompleteProvider::DeclarationType::Variable, {} });
|
||||
}
|
||||
}
|
||||
::Shell::AST::NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void visit(const ::Shell::AST::FunctionDeclaration* node) override
|
||||
{
|
||||
dbgln("Found function {}", node->name().name);
|
||||
declarations.append({ node->name().name, { filename, node->position().start_line.line_number, node->position().start_line.line_column }, GUI::AutocompleteProvider::DeclarationType::Function, {} });
|
||||
}
|
||||
|
||||
String const& filename;
|
||||
Vector<GUI::AutocompleteProvider::Declaration> declarations;
|
||||
} visitor { document.filename };
|
||||
|
||||
document.node->visit(visitor);
|
||||
|
||||
set_declarations_of_document(document.filename, move(visitor.declarations));
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <DevTools/HackStudio/LanguageServers/CodeComprehensionEngine.h>
|
||||
#include <Shell/Shell.h>
|
||||
|
||||
namespace LanguageServers::Shell {
|
||||
|
||||
class ShellComprehensionEngine : public CodeComprehensionEngine {
|
||||
public:
|
||||
ShellComprehensionEngine(FileDB const& filedb);
|
||||
virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(String const& file, const GUI::TextPosition& position) override;
|
||||
virtual void on_edit(String const& file) override;
|
||||
virtual void file_opened([[maybe_unused]] String const& file) override;
|
||||
virtual Optional<GUI::AutocompleteProvider::ProjectLocation> find_declaration_of(String const& filename, const GUI::TextPosition& identifier_position) override;
|
||||
|
||||
private:
|
||||
struct DocumentData {
|
||||
DocumentData(String&& text, String filename);
|
||||
String filename;
|
||||
String text;
|
||||
NonnullRefPtr<::Shell::AST::Node> node;
|
||||
|
||||
Vector<String> const& sourced_paths() const;
|
||||
|
||||
private:
|
||||
NonnullRefPtr<::Shell::AST::Node> parse() const;
|
||||
|
||||
mutable Optional<Vector<String>> all_sourced_paths {};
|
||||
};
|
||||
|
||||
DocumentData const& get_document_data(String const& file) const;
|
||||
DocumentData const& get_or_create_document_data(String const& file);
|
||||
void set_document_data(String const& file, OwnPtr<DocumentData>&& data);
|
||||
|
||||
OwnPtr<DocumentData> create_document_data_for(String const& file);
|
||||
String document_path_from_include_path(StringView include_path) const;
|
||||
void update_declared_symbols(DocumentData const&);
|
||||
|
||||
static size_t resolve(ShellComprehensionEngine::DocumentData const& document, const GUI::TextPosition& position);
|
||||
|
||||
::Shell::Shell& shell()
|
||||
{
|
||||
if (s_shell)
|
||||
return *s_shell;
|
||||
s_shell = ::Shell::Shell::construct();
|
||||
return *s_shell;
|
||||
}
|
||||
|
||||
HashMap<String, OwnPtr<DocumentData>> m_documents;
|
||||
static RefPtr<::Shell::Shell> s_shell;
|
||||
};
|
||||
}
|
|
@ -22,13 +22,13 @@ class LocatorSuggestionModel final : public GUI::Model {
|
|||
public:
|
||||
struct Suggestion {
|
||||
static Suggestion create_filename(String const& filename);
|
||||
static Suggestion create_symbol_declaration(const GUI::AutocompleteProvider::Declaration&);
|
||||
static Suggestion create_symbol_declaration(CodeComprehension::Declaration const&);
|
||||
|
||||
bool is_filename() const { return as_filename.has_value(); }
|
||||
bool is_symbol_declaration() const { return as_symbol_declaration.has_value(); }
|
||||
|
||||
Optional<String> as_filename;
|
||||
Optional<GUI::AutocompleteProvider::Declaration> as_symbol_declaration;
|
||||
Optional<CodeComprehension::Declaration> as_symbol_declaration;
|
||||
};
|
||||
|
||||
explicit LocatorSuggestionModel(Vector<Suggestion>&& suggestions)
|
||||
|
@ -88,7 +88,7 @@ LocatorSuggestionModel::Suggestion LocatorSuggestionModel::Suggestion::create_fi
|
|||
s.as_filename = filename;
|
||||
return s;
|
||||
}
|
||||
LocatorSuggestionModel::Suggestion LocatorSuggestionModel::Suggestion::create_symbol_declaration(const GUI::AutocompleteProvider::Declaration& decl)
|
||||
LocatorSuggestionModel::Suggestion LocatorSuggestionModel::Suggestion::create_symbol_declaration(CodeComprehension::Declaration const& decl)
|
||||
{
|
||||
LocatorSuggestionModel::Suggestion s;
|
||||
s.as_symbol_declaration = decl;
|
||||
|
|
|
@ -11,14 +11,14 @@ HackStudio::ProjectDeclarations& HackStudio::ProjectDeclarations::the()
|
|||
static ProjectDeclarations s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
void HackStudio::ProjectDeclarations::set_declared_symbols(String const& filename, Vector<GUI::AutocompleteProvider::Declaration> const& declarations)
|
||||
void HackStudio::ProjectDeclarations::set_declared_symbols(String const& filename, Vector<CodeComprehension::Declaration> const& declarations)
|
||||
{
|
||||
m_document_to_declarations.set(filename, declarations);
|
||||
if (on_update)
|
||||
on_update();
|
||||
}
|
||||
|
||||
Optional<GUI::Icon> HackStudio::ProjectDeclarations::get_icon_for(GUI::AutocompleteProvider::DeclarationType type)
|
||||
Optional<GUI::Icon> HackStudio::ProjectDeclarations::get_icon_for(CodeComprehension::DeclarationType type)
|
||||
{
|
||||
static GUI::Icon struct_icon(Gfx::Bitmap::try_load_from_file("/res/icons/hackstudio/Struct.png").release_value_but_fixme_should_propagate_errors());
|
||||
static GUI::Icon class_icon(Gfx::Bitmap::try_load_from_file("/res/icons/hackstudio/Class.png").release_value_but_fixme_should_propagate_errors());
|
||||
|
@ -28,19 +28,19 @@ Optional<GUI::Icon> HackStudio::ProjectDeclarations::get_icon_for(GUI::Autocompl
|
|||
static GUI::Icon member_icon(Gfx::Bitmap::try_load_from_file("/res/icons/hackstudio/Member.png").release_value_but_fixme_should_propagate_errors());
|
||||
static GUI::Icon namespace_icon(Gfx::Bitmap::try_load_from_file("/res/icons/hackstudio/Namespace.png").release_value_but_fixme_should_propagate_errors());
|
||||
switch (type) {
|
||||
case GUI::AutocompleteProvider::DeclarationType::Struct:
|
||||
case CodeComprehension::DeclarationType::Struct:
|
||||
return struct_icon;
|
||||
case GUI::AutocompleteProvider::DeclarationType::Class:
|
||||
case CodeComprehension::DeclarationType::Class:
|
||||
return class_icon;
|
||||
case GUI::AutocompleteProvider::DeclarationType::Function:
|
||||
case CodeComprehension::DeclarationType::Function:
|
||||
return function_icon;
|
||||
case GUI::AutocompleteProvider::DeclarationType::Variable:
|
||||
case CodeComprehension::DeclarationType::Variable:
|
||||
return variable_icon;
|
||||
case GUI::AutocompleteProvider::DeclarationType::PreprocessorDefinition:
|
||||
case CodeComprehension::DeclarationType::PreprocessorDefinition:
|
||||
return preprocessor_icon;
|
||||
case GUI::AutocompleteProvider::DeclarationType::Member:
|
||||
case CodeComprehension::DeclarationType::Member:
|
||||
return member_icon;
|
||||
case GUI::AutocompleteProvider::DeclarationType::Namespace:
|
||||
case CodeComprehension::DeclarationType::Namespace:
|
||||
return namespace_icon;
|
||||
default:
|
||||
return {};
|
||||
|
|
|
@ -23,15 +23,15 @@ public:
|
|||
template<typename Func>
|
||||
void for_each_declared_symbol(Func);
|
||||
|
||||
void set_declared_symbols(String const& filename, Vector<GUI::AutocompleteProvider::Declaration> const&);
|
||||
void set_declared_symbols(String const& filename, Vector<CodeComprehension::Declaration> const&);
|
||||
|
||||
static Optional<GUI::Icon> get_icon_for(GUI::AutocompleteProvider::DeclarationType);
|
||||
static Optional<GUI::Icon> get_icon_for(CodeComprehension::DeclarationType);
|
||||
|
||||
Function<void()> on_update = nullptr;
|
||||
|
||||
private:
|
||||
ProjectDeclarations() = default;
|
||||
HashMap<String, Vector<GUI::AutocompleteProvider::Declaration>> m_document_to_declarations;
|
||||
HashMap<String, Vector<CodeComprehension::Declaration>> m_document_to_declarations;
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
|
|
|
@ -14,16 +14,16 @@ ToDoEntries& HackStudio::ToDoEntries::the()
|
|||
return s_instance;
|
||||
}
|
||||
|
||||
void ToDoEntries::set_entries(String const& filename, Vector<Cpp::Parser::TodoEntry> const&& entries)
|
||||
void ToDoEntries::set_entries(String const& filename, Vector<CodeComprehension::TodoEntry> const&& entries)
|
||||
{
|
||||
m_document_to_entries.set(filename, move(entries));
|
||||
if (on_update)
|
||||
on_update();
|
||||
}
|
||||
|
||||
Vector<Cpp::Parser::TodoEntry> ToDoEntries::get_entries()
|
||||
Vector<CodeComprehension::TodoEntry> ToDoEntries::get_entries()
|
||||
{
|
||||
Vector<Cpp::Parser::TodoEntry> ret;
|
||||
Vector<CodeComprehension::TodoEntry> ret;
|
||||
for (auto& it : m_document_to_entries) {
|
||||
for (auto& entry : it.value)
|
||||
ret.append({ entry.content, it.key, entry.line, entry.column });
|
||||
|
|
|
@ -20,9 +20,9 @@ class ToDoEntries {
|
|||
public:
|
||||
static ToDoEntries& the();
|
||||
|
||||
void set_entries(String const& filename, Vector<Cpp::Parser::TodoEntry> const&& entries);
|
||||
void set_entries(String const& filename, Vector<CodeComprehension::TodoEntry> const&& entries);
|
||||
|
||||
Vector<Cpp::Parser::TodoEntry> get_entries();
|
||||
Vector<CodeComprehension::TodoEntry> get_entries();
|
||||
|
||||
void clear_entries();
|
||||
|
||||
|
@ -30,7 +30,7 @@ public:
|
|||
|
||||
private:
|
||||
ToDoEntries() = default;
|
||||
HashMap<String, Vector<Cpp::Parser::TodoEntry>> m_document_to_entries;
|
||||
HashMap<String, Vector<CodeComprehension::TodoEntry>> m_document_to_entries;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
__Count
|
||||
};
|
||||
|
||||
explicit ToDoEntriesModel(Vector<Cpp::Parser::TodoEntry> const&& matches)
|
||||
explicit ToDoEntriesModel(Vector<CodeComprehension::TodoEntry> const&& matches)
|
||||
: m_matches(move(matches))
|
||||
{
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
Vector<Cpp::Parser::TodoEntry> m_matches;
|
||||
Vector<CodeComprehension::TodoEntry> m_matches;
|
||||
};
|
||||
|
||||
void ToDoEntriesWidget::refresh()
|
||||
|
@ -103,7 +103,7 @@ ToDoEntriesWidget::ToDoEntriesWidget()
|
|||
m_result_view = add<GUI::TableView>();
|
||||
|
||||
m_result_view->on_activation = [](auto& index) {
|
||||
auto& match = *(Cpp::Parser::TodoEntry const*)index.internal_data();
|
||||
auto& match = *(CodeComprehension::TodoEntry const*)index.internal_data();
|
||||
open_file(match.filename, match.line, match.column);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue