mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:38:11 +00:00
HackStudio: Integate with C++ parser-based autocomplete
By default, C++ auto completion will still be performed by the lexer-based logic. However, the parser-based logic can be switched on via the menubar.
This commit is contained in:
parent
8ed96eb27c
commit
fa18010477
14 changed files with 70 additions and 11 deletions
|
@ -1,6 +1,6 @@
|
||||||
|
#include "other.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "other.h"
|
|
||||||
|
|
||||||
enum TestEnum {
|
enum TestEnum {
|
||||||
ValueOne,
|
ValueOne,
|
||||||
|
@ -13,11 +13,17 @@ struct MyStruct {
|
||||||
TestEnum test_value { ValueOne };
|
TestEnum test_value { ValueOne };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Container {
|
||||||
|
MyStruct inner;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
int main(int, char**)
|
int main(int, char**)
|
||||||
{
|
{
|
||||||
MyStruct my_struct;
|
MyStruct my_struct;
|
||||||
my_struct.status = !my_struct.status;
|
my_struct.status = !my_struct.status;
|
||||||
printf("my_struct.x is %d\n", my_struct.x);
|
printf("my_struct.x is %d\n", my_struct.x);
|
||||||
|
Container container;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
// This is a comment :^)
|
// This is a comment :^)
|
||||||
func();
|
func();
|
||||||
|
|
|
@ -160,6 +160,9 @@ set(VOLATILE_PAGE_RANGES_DEBUG ON)
|
||||||
set(WSMESSAGELOOP_DEBUG ON)
|
set(WSMESSAGELOOP_DEBUG ON)
|
||||||
set(GPT_DEBUG ON)
|
set(GPT_DEBUG ON)
|
||||||
set(CPP_DEBUG ON)
|
set(CPP_DEBUG ON)
|
||||||
|
set(DEBUG_SPAM ON)
|
||||||
|
set(DEBUG_CPP_LANGUAGE_SERVER ON)
|
||||||
|
set(DEBUG_AUTOCOMPLETE ON)
|
||||||
|
|
||||||
# False positive: DEBUG is a flag but it works differently.
|
# False positive: DEBUG is a flag but it works differently.
|
||||||
# set(DEBUG ON)
|
# set(DEBUG ON)
|
||||||
|
|
|
@ -841,6 +841,7 @@ void HackStudioWidget::create_project_menubar(GUI::MenuBar& menubar)
|
||||||
{
|
{
|
||||||
auto& project_menu = menubar.add_menu("Project");
|
auto& project_menu = menubar.add_menu("Project");
|
||||||
project_menu.add_action(*m_new_action);
|
project_menu.add_action(*m_new_action);
|
||||||
|
project_menu.add_action(*create_set_autocomplete_mode_action());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HackStudioWidget::create_edit_menubar(GUI::MenuBar& menubar)
|
void HackStudioWidget::create_edit_menubar(GUI::MenuBar& menubar)
|
||||||
|
@ -919,6 +920,14 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_stop_action()
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<GUI::Action> HackStudioWidget::create_set_autocomplete_mode_action()
|
||||||
|
{
|
||||||
|
auto action = GUI::Action::create_checkable("AutoComplete C++ with Parser", [this](auto& action) {
|
||||||
|
get_language_client<LanguageClients::Cpp::ServerConnection>(project().root_path())->set_autocomplete_mode(action.is_checked() ? "Parser" : "Lexer");
|
||||||
|
});
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
void HackStudioWidget::initialize_menubar(GUI::MenuBar& menubar)
|
void HackStudioWidget::initialize_menubar(GUI::MenuBar& menubar)
|
||||||
{
|
{
|
||||||
create_app_menubar(menubar);
|
create_app_menubar(menubar);
|
||||||
|
|
|
@ -94,6 +94,7 @@ private:
|
||||||
NonnullRefPtr<GUI::Action> create_build_action();
|
NonnullRefPtr<GUI::Action> create_build_action();
|
||||||
NonnullRefPtr<GUI::Action> create_run_action();
|
NonnullRefPtr<GUI::Action> create_run_action();
|
||||||
NonnullRefPtr<GUI::Action> create_stop_action();
|
NonnullRefPtr<GUI::Action> create_stop_action();
|
||||||
|
NonnullRefPtr<GUI::Action> create_set_autocomplete_mode_action();
|
||||||
|
|
||||||
void add_new_editor(GUI::Widget& parent);
|
void add_new_editor(GUI::Widget& parent);
|
||||||
NonnullRefPtr<EditorWrapper> get_editor_of_file(const String& file_name);
|
NonnullRefPtr<EditorWrapper> get_editor_of_file(const String& file_name);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "LanguageClient.h"
|
#include "LanguageClient.h"
|
||||||
|
#include "DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h"
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
|
||||||
|
@ -72,4 +73,9 @@ void LanguageClient::provide_autocomplete_suggestions(const Vector<GUI::Autocomp
|
||||||
// Otherwise, drop it on the floor :shrug:
|
// Otherwise, drop it on the floor :shrug:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageClient::set_autocomplete_mode(const String& mode)
|
||||||
|
{
|
||||||
|
m_connection.post_message(Messages::LanguageServer::SetAutoCompleteMode(mode));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ public:
|
||||||
virtual void insert_text(const String& path, const String& text, size_t line, size_t column);
|
virtual void insert_text(const String& path, const String& text, size_t line, size_t column);
|
||||||
virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
|
virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
|
||||||
virtual void request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column);
|
virtual void request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column);
|
||||||
|
virtual void set_autocomplete_mode(const String& mode);
|
||||||
|
|
||||||
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&);
|
void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
ClientConnection.cpp
|
ClientConnection.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
AutoComplete.cpp
|
LexerAutoComplete.cpp
|
||||||
|
ParserAutoComplete.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(GENERATED_SOURCES
|
set(GENERATED_SOURCES
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ClientConnection.h"
|
#include "ClientConnection.h"
|
||||||
#include "AutoComplete.h"
|
#include "LexerAutoComplete.h"
|
||||||
|
#include "ParserAutoComplete.h"
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
|
@ -146,7 +147,16 @@ void ClientConnection::handle(const Messages::LanguageServer::AutoCompleteSugges
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto suggestions = AutoComplete::get_suggestions(document->text(), { (size_t)message.cursor_line(), (size_t)max(message.cursor_column(), message.cursor_column() - 1) });
|
Vector<GUI::AutocompleteProvider::Entry> suggestions;
|
||||||
|
switch (m_auto_complete_mode) {
|
||||||
|
case AutoCompleteMode::Lexer:
|
||||||
|
suggestions = LexerAutoComplete::get_suggestions(document->text(), { (size_t)message.cursor_line(), (size_t)max(message.cursor_column(), message.cursor_column() - 1) });
|
||||||
|
break;
|
||||||
|
case AutoCompleteMode::Parser: {
|
||||||
|
auto engine = ParserAutoComplete(document->text());
|
||||||
|
suggestions = engine.get_suggestions({ (size_t)message.cursor_line(), (size_t)max(message.cursor_column(), message.cursor_column() - 1) });
|
||||||
|
}
|
||||||
|
}
|
||||||
post_message(Messages::LanguageClient::AutoCompleteSuggestions(move(suggestions)));
|
post_message(Messages::LanguageClient::AutoCompleteSuggestions(move(suggestions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,4 +180,15 @@ void ClientConnection::handle(const Messages::LanguageServer::SetFileContent& me
|
||||||
document->set_text(content.view());
|
document->set_text(content.view());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientConnection::handle(const Messages::LanguageServer::SetAutoCompleteMode& message)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CPP_LANGUAGE_SERVER
|
||||||
|
dbgln("SetAutoCompleteMode: {}", message.mode());
|
||||||
|
#endif
|
||||||
|
if (message.mode() == "Parser")
|
||||||
|
m_auto_complete_mode = AutoCompleteMode::Parser;
|
||||||
|
else
|
||||||
|
m_auto_complete_mode = AutoCompleteMode::Lexer;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,18 @@ private:
|
||||||
virtual void handle(const Messages::LanguageServer::FileEditRemoveText&) override;
|
virtual void handle(const Messages::LanguageServer::FileEditRemoveText&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
||||||
|
virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override;
|
||||||
|
|
||||||
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
||||||
|
|
||||||
HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
|
HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
|
||||||
|
|
||||||
|
enum class AutoCompleteMode {
|
||||||
|
Lexer,
|
||||||
|
Parser
|
||||||
|
};
|
||||||
|
|
||||||
|
AutoCompleteMode m_auto_complete_mode { AutoCompleteMode::Lexer };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,14 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AutoComplete.h"
|
#include "LexerAutoComplete.h"
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/HashTable.h>
|
#include <AK/HashTable.h>
|
||||||
#include <LibCpp/Lexer.h>
|
#include <LibCpp/Lexer.h>
|
||||||
|
|
||||||
namespace LanguageServers::Cpp {
|
namespace LanguageServers::Cpp {
|
||||||
|
|
||||||
Vector<GUI::AutocompleteProvider::Entry> AutoComplete::get_suggestions(const String& code, const GUI::TextPosition& autocomplete_position)
|
Vector<GUI::AutocompleteProvider::Entry> LexerAutoComplete::get_suggestions(const String& code, const GUI::TextPosition& autocomplete_position)
|
||||||
{
|
{
|
||||||
auto lines = code.split('\n', true);
|
auto lines = code.split('\n', true);
|
||||||
Cpp::Lexer lexer(code);
|
Cpp::Lexer lexer(code);
|
||||||
|
@ -52,14 +52,14 @@ Vector<GUI::AutocompleteProvider::Entry> AutoComplete::get_suggestions(const Str
|
||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView AutoComplete::text_of_token(const Vector<String>& lines, const Cpp::Token& token)
|
StringView LexerAutoComplete::text_of_token(const Vector<String>& lines, const Cpp::Token& token)
|
||||||
{
|
{
|
||||||
ASSERT(token.m_start.line == token.m_end.line);
|
ASSERT(token.m_start.line == token.m_end.line);
|
||||||
ASSERT(token.m_start.column <= token.m_end.column);
|
ASSERT(token.m_start.column <= token.m_end.column);
|
||||||
return lines[token.m_start.line].substring_view(token.m_start.column, token.m_end.column - token.m_start.column + 1);
|
return lines[token.m_start.line].substring_view(token.m_start.column, token.m_end.column - token.m_start.column + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<size_t> AutoComplete::token_in_position(const Vector<Cpp::Token>& tokens, const GUI::TextPosition& position)
|
Optional<size_t> LexerAutoComplete::token_in_position(const Vector<Cpp::Token>& tokens, const GUI::TextPosition& position)
|
||||||
{
|
{
|
||||||
for (size_t token_index = 0; token_index < tokens.size(); ++token_index) {
|
for (size_t token_index = 0; token_index < tokens.size(); ++token_index) {
|
||||||
auto& token = tokens[token_index];
|
auto& token = tokens[token_index];
|
||||||
|
@ -74,7 +74,7 @@ Optional<size_t> AutoComplete::token_in_position(const Vector<Cpp::Token>& token
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<GUI::AutocompleteProvider::Entry> AutoComplete::identifier_prefixes(const Vector<String>& lines, const Vector<Cpp::Token>& tokens, size_t target_token_index)
|
Vector<GUI::AutocompleteProvider::Entry> LexerAutoComplete::identifier_prefixes(const Vector<String>& lines, const Vector<Cpp::Token>& tokens, size_t target_token_index)
|
||||||
{
|
{
|
||||||
auto partial_input = text_of_token(lines, tokens[target_token_index]);
|
auto partial_input = text_of_token(lines, tokens[target_token_index]);
|
||||||
Vector<GUI::AutocompleteProvider::Entry> suggestions;
|
Vector<GUI::AutocompleteProvider::Entry> suggestions;
|
|
@ -36,9 +36,9 @@ namespace LanguageServers::Cpp {
|
||||||
|
|
||||||
using namespace ::Cpp;
|
using namespace ::Cpp;
|
||||||
|
|
||||||
class AutoComplete {
|
class LexerAutoComplete {
|
||||||
public:
|
public:
|
||||||
AutoComplete() = delete;
|
LexerAutoComplete() = delete;
|
||||||
|
|
||||||
static Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& code, const GUI::TextPosition& autocomplete_position);
|
static Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& code, const GUI::TextPosition& autocomplete_position);
|
||||||
|
|
|
@ -8,4 +8,5 @@ endpoint LanguageServer = 8001
|
||||||
SetFileContent(String file_name, String content) =|
|
SetFileContent(String file_name, String content) =|
|
||||||
|
|
||||||
AutoCompleteSuggestions(String file_name, i32 cursor_line, i32 cursor_column) =|
|
AutoCompleteSuggestions(String file_name, i32 cursor_line, i32 cursor_column) =|
|
||||||
|
SetAutoCompleteMode(String mode) =|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ private:
|
||||||
virtual void handle(const Messages::LanguageServer::FileEditRemoveText&) override;
|
virtual void handle(const Messages::LanguageServer::FileEditRemoveText&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
virtual void handle(const Messages::LanguageServer::SetFileContent&) override;
|
||||||
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
|
||||||
|
virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override { }
|
||||||
|
|
||||||
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,7 @@ AutocompleteBox::AutocompleteBox(TextEditor& editor)
|
||||||
|
|
||||||
m_suggestion_view = m_popup_window->set_main_widget<GUI::TableView>();
|
m_suggestion_view = m_popup_window->set_main_widget<GUI::TableView>();
|
||||||
m_suggestion_view->set_column_headers_visible(false);
|
m_suggestion_view->set_column_headers_visible(false);
|
||||||
|
m_suggestion_view->set_column_width(1, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutocompleteBox::update_suggestions(Vector<AutocompleteProvider::Entry>&& suggestions)
|
void AutocompleteBox::update_suggestions(Vector<AutocompleteProvider::Entry>&& suggestions)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue