summaryrefslogtreecommitdiff
path: root/DevTools
diff options
context:
space:
mode:
Diffstat (limited to 'DevTools')
-rw-r--r--DevTools/HackStudio/Editor.cpp9
-rw-r--r--DevTools/HackStudio/HackStudioWidget.cpp80
-rw-r--r--DevTools/HackStudio/HackStudioWidget.h5
-rw-r--r--DevTools/HackStudio/Project.cpp365
-rw-r--r--DevTools/HackStudio/Project.h60
-rw-r--r--DevTools/HackStudio/main.cpp29
6 files changed, 83 insertions, 465 deletions
diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp
index 7a6972fb08..660ff1c5a2 100644
--- a/DevTools/HackStudio/Editor.cpp
+++ b/DevTools/HackStudio/Editor.cpp
@@ -465,7 +465,7 @@ void Editor::set_document(GUI::TextDocument& doc)
switch (code_document.language()) {
case Language::Cpp:
set_syntax_highlighter(make<GUI::CppSyntaxHighlighter>());
- m_language_client = get_language_client<LanguageClients::Cpp::ServerConnection>(project().root_directory());
+ m_language_client = get_language_client<LanguageClients::Cpp::ServerConnection>(project().root_path());
break;
case Language::JavaScript:
set_syntax_highlighter(make<GUI::JSSyntaxHighlighter>());
@@ -475,16 +475,15 @@ void Editor::set_document(GUI::TextDocument& doc)
break;
case Language::Shell:
set_syntax_highlighter(make<GUI::ShellSyntaxHighlighter>());
- m_language_client = get_language_client<LanguageClients::Shell::ServerConnection>(project().root_directory());
+ m_language_client = get_language_client<LanguageClients::Shell::ServerConnection>(project().root_path());
break;
default:
set_syntax_highlighter(nullptr);
}
if (m_language_client) {
- auto full_file_path = String::formatted("{}/{}", project().root_directory(), code_document.file_path());
- dbg() << "Opening " << full_file_path;
- int fd = open(full_file_path.characters(), O_RDONLY | O_NOCTTY);
+ dbgln("Opening {}", code_document.file_path());
+ int fd = open(code_document.file_path().string().characters(), O_RDONLY | O_NOCTTY);
if (fd < 0) {
perror("open");
return;
diff --git a/DevTools/HackStudio/HackStudioWidget.cpp b/DevTools/HackStudio/HackStudioWidget.cpp
index 2ecf6f808e..e373664110 100644
--- a/DevTools/HackStudio/HackStudioWidget.cpp
+++ b/DevTools/HackStudio/HackStudioWidget.cpp
@@ -109,6 +109,10 @@ HackStudioWidget::HackStudioWidget(const String& path_to_project)
m_right_hand_splitter = outer_splitter.add<GUI::VerticalSplitter>();
m_right_hand_stack = m_right_hand_splitter->add<GUI::StackWidget>();
+
+ // Put a placeholder widget front & center since we don't have a file open yet.
+ m_right_hand_stack->add<GUI::Widget>();
+
create_form_editor(*m_right_hand_stack);
m_diff_viewer = m_right_hand_stack->add<DiffViewer>();
@@ -174,14 +178,13 @@ void HackStudioWidget::on_action_tab_change()
reinterpret_cast<GitWidget*>(git_widget)->refresh();
}
-void HackStudioWidget::open_project(String filename)
+void HackStudioWidget::open_project(const String& root_path)
{
- LexicalPath lexical_path(filename);
- if (chdir(lexical_path.dirname().characters()) < 0) {
+ if (chdir(root_path.characters()) < 0) {
perror("chdir");
exit(1);
}
- m_project = Project::load_from_file(filename);
+ m_project = Project::open_with_root_path(root_path);
ASSERT(m_project);
if (m_project_tree_view) {
m_project_tree_view->set_model(m_project->model());
@@ -240,7 +243,12 @@ void HackStudioWidget::open_file(const String& filename)
}
m_currently_open_file = filename;
- window()->set_title(String::formatted("{} - HackStudio", m_currently_open_file));
+
+ String relative_file_path = m_currently_open_file;
+ if (m_currently_open_file.starts_with(m_project->root_path()))
+ relative_file_path = m_currently_open_file.substring(m_project->root_path().length() + 1);
+
+ window()->set_title(String::formatted("{} - {} - HackStudio", relative_file_path, m_project->name()));
m_project_tree_view->update();
current_editor_wrapper().filename_label().set_text(filename);
@@ -277,14 +285,12 @@ NonnullRefPtr<GUI::Menu> HackStudioWidget::create_project_tree_view_context_menu
{
m_open_selected_action = create_open_selected_action();
m_new_action = create_new_action();
- m_add_existing_file_action = create_add_existing_file_action();
m_delete_action = create_delete_action();
auto project_tree_view_context_menu = GUI::Menu::construct("Project Files");
project_tree_view_context_menu->add_action(*m_open_selected_action);
// TODO: Rename, cut, copy, duplicate with new name, show containing folder ...
project_tree_view_context_menu->add_separator();
project_tree_view_context_menu->add_action(*m_new_action);
- project_tree_view_context_menu->add_action(*m_add_existing_file_action);
project_tree_view_context_menu->add_action(*m_delete_action);
return project_tree_view_context_menu;
}
@@ -300,11 +306,6 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_new_action()
GUI::MessageBox::show(window(), String::formatted("Failed to create '{}'", filename), "Error", GUI::MessageBox::Type::Error);
return;
}
- if (!m_project->add_file(filename)) {
- GUI::MessageBox::show(window(), String::formatted("Failed to add '{}' to project", filename), "Error", GUI::MessageBox::Type::Error);
- // FIXME: Should we unlink the file here maybe?
- return;
- }
m_project_tree_view->toggle_index(m_project_tree_view->model()->index(0, 0));
open_file(filename);
});
@@ -322,25 +323,8 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_open_selected_action()
return open_selected_action;
}
-NonnullRefPtr<GUI::Action> HackStudioWidget::create_add_existing_file_action()
-{
- return GUI::Action::create("Add existing file to project...", Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"), [this](auto&) {
- auto result = GUI::FilePicker::get_open_filepath(window(), "Add existing file to project");
- if (!result.has_value())
- return;
- auto& filename = result.value();
- if (!m_project->add_file(filename)) {
- GUI::MessageBox::show(window(), String::formatted("Failed to add '{}' to project", filename), "Error", GUI::MessageBox::Type::Error);
- return;
- }
- m_project_tree_view->toggle_index(m_project_tree_view->model()->index(0, 0));
- open_file(filename);
- });
-}
-
NonnullRefPtr<GUI::Action> HackStudioWidget::create_delete_action()
{
-
auto delete_action = GUI::CommonActions::make_delete_action([this](const GUI::Action&) {
auto files = selected_file_names();
if (files.is_empty())
@@ -348,9 +332,9 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_delete_action()
String message;
if (files.size() == 1) {
- message = String::formatted("Really remove {} from the project?", LexicalPath(files[0]).basename());
+ message = String::formatted("Really remove {} from disk?", LexicalPath(files[0]).basename());
} else {
- message = String::formatted("Really remove {} files from the project?", files.size());
+ message = String::formatted("Really remove {} files from disk?", files.size());
}
auto result = GUI::MessageBox::show(window(),
@@ -362,11 +346,8 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_delete_action()
return;
for (auto& file : files) {
- if (m_project->remove_file(file)) {
- m_open_files_vector.remove_first_matching([&](auto& filename) {
- return filename == file;
- });
- m_open_files_view->model()->update();
+ if (1) {
+ // FIXME: Remove `file` from disk
} else {
GUI::MessageBox::show(window(),
String::formatted("Removing file {} from the project failed.", file),
@@ -455,7 +436,6 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_open_action()
if (!open_path.has_value())
return;
open_project(open_path.value());
- open_file(m_project->default_file());
update_actions();
});
}
@@ -522,10 +502,6 @@ void HackStudioWidget::reveal_action_tab(GUI::Widget& widget)
NonnullRefPtr<GUI::Action> HackStudioWidget::create_debug_action()
{
return GUI::Action::create("Debug", Gfx::Bitmap::load_from_file("/res/icons/16x16/debug-run.png"), [this](auto&) {
- if (m_project->type() != ProjectType::Cpp) {
- GUI::MessageBox::show(window(), "Cannot debug current project type", "Error", GUI::MessageBox::Type::Error);
- return;
- }
if (!GUI::FilePicker::file_exists(get_project_executable_path())) {
GUI::MessageBox::show(window(), String::formatted("Could not find file: {}. (did you build the project?)", get_project_executable_path()), "Error", GUI::MessageBox::Type::Error);
return;
@@ -534,6 +510,7 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_debug_action()
GUI::MessageBox::show(window(), "Debugger is already running", "Error", GUI::MessageBox::Type::Error);
return;
}
+
Debugger::the().set_executable_path(get_project_executable_path());
m_debugger_thread = adopt(*new LibThread::Thread(Debugger::start_static));
m_debugger_thread->start();
@@ -616,14 +593,15 @@ NonnullRefPtr<EditorWrapper> HackStudioWidget::get_editor_of_file(const String&
String HackStudioWidget::get_project_executable_path() const
{
- // e.g /my/project.hsp => /my/project
+ // FIXME: Dumb heuristic ahead!
+ // e.g /my/project => /my/project/project
// TODO: Perhaps a Makefile rule for getting the value of $(PROGRAM) would be better?
- return m_project->path().substring(0, m_project->path().index_of(".").value());
+ return String::formatted("{}/{}", m_project->root_path(), LexicalPath(m_project->root_path()).basename());
}
void HackStudioWidget::build(TerminalWrapper& wrapper)
{
- if (m_project->type() == ProjectType::JavaScript && m_currently_open_file.ends_with(".js"))
+ if (m_currently_open_file.ends_with(".js"))
wrapper.run_command(String::formatted("js -A {}", m_currently_open_file));
else
wrapper.run_command("make");
@@ -631,7 +609,7 @@ void HackStudioWidget::build(TerminalWrapper& wrapper)
void HackStudioWidget::run(TerminalWrapper& wrapper)
{
- if (m_project->type() == ProjectType::JavaScript && m_currently_open_file.ends_with(".js"))
+ if (m_currently_open_file.ends_with(".js"))
wrapper.run_command(String::formatted("js {}", m_currently_open_file));
else
wrapper.run_command("make run");
@@ -656,7 +634,11 @@ void HackStudioWidget::create_project_tree_view(GUI::Widget& parent)
{
m_project_tree_view = parent.add<GUI::TreeView>();
m_project_tree_view->set_model(m_project->model());
- m_project_tree_view->toggle_index(m_project_tree_view->model()->index(0, 0));
+
+ for (int column_index = 0; column_index < m_project->model().column_count(); ++column_index)
+ m_project_tree_view->set_column_hidden(column_index, true);
+
+ m_project_tree_view->set_column_hidden(GUI::FileSystemModel::Column::Name, false);
m_project_tree_view->on_context_menu_request = [this](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
if (index.is_valid()) {
@@ -777,7 +759,6 @@ void HackStudioWidget::create_toolbar(GUI::Widget& parent)
{
auto& toolbar = parent.add<GUI::ToolBar>();
toolbar.add_action(*m_new_action);
- toolbar.add_action(*m_add_existing_file_action);
toolbar.add_action(*m_save_action);
toolbar.add_action(*m_delete_action);
toolbar.add_separator();
@@ -837,7 +818,7 @@ void HackStudioWidget::create_action_tab(GUI::Widget& parent)
m_terminal_wrapper = m_action_tab_widget->add_tab<TerminalWrapper>("Build", false);
m_debug_info_widget = m_action_tab_widget->add_tab<DebugInfoWidget>("Debug");
m_disassembly_widget = m_action_tab_widget->add_tab<DisassemblyWidget>("Disassembly");
- m_git_widget = m_action_tab_widget->add_tab<GitWidget>("Git", LexicalPath(m_project->root_directory()));
+ m_git_widget = m_action_tab_widget->add_tab<GitWidget>("Git", LexicalPath(m_project->root_path()));
m_git_widget->set_view_diff_callback([this](const auto& original_content, const auto& diff) {
m_diff_viewer->set_content(original_content, diff);
set_edit_mode(EditMode::Diff);
@@ -850,7 +831,7 @@ void HackStudioWidget::create_app_menubar(GUI::MenuBar& menubar)
app_menu.add_action(*m_open_action);
app_menu.add_action(*m_save_action);
app_menu.add_separator();
- app_menu.add_action(GUI::CommonActions::make_quit_action([this](auto&) {
+ app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
GUI::Application::the()->quit();
}));
}
@@ -859,7 +840,6 @@ void HackStudioWidget::create_project_menubar(GUI::MenuBar& menubar)
{
auto& project_menu = menubar.add_menu("Project");
project_menu.add_action(*m_new_action);
- project_menu.add_action(*m_add_existing_file_action);
}
void HackStudioWidget::create_edit_menubar(GUI::MenuBar& menubar)
diff --git a/DevTools/HackStudio/HackStudioWidget.h b/DevTools/HackStudio/HackStudioWidget.h
index f182da1466..29ef3701e4 100644
--- a/DevTools/HackStudio/HackStudioWidget.h
+++ b/DevTools/HackStudio/HackStudioWidget.h
@@ -37,6 +37,7 @@
#include "Git/GitWidget.h"
#include "Locator.h"
#include "Project.h"
+#include "ProjectFile.h"
#include "TerminalWrapper.h"
#include <LibGUI/ScrollBar.h>
#include <LibGUI/Splitter.h>
@@ -67,7 +68,7 @@ private:
static String get_full_path_of_serenity_source(const String& file);
HackStudioWidget(const String& path_to_project);
- void open_project(String filename);
+ void open_project(const String& root_path);
enum class EditMode {
Text,
@@ -80,7 +81,6 @@ private:
NonnullRefPtr<GUI::Menu> create_project_tree_view_context_menu();
NonnullRefPtr<GUI::Action> create_new_action();
NonnullRefPtr<GUI::Action> create_open_selected_action();
- NonnullRefPtr<GUI::Action> create_add_existing_file_action();
NonnullRefPtr<GUI::Action> create_delete_action();
NonnullRefPtr<GUI::Action> create_switch_to_next_editor_action();
NonnullRefPtr<GUI::Action> create_switch_to_previous_editor_action();
@@ -153,7 +153,6 @@ private:
RefPtr<GUI::Action> m_new_action;
RefPtr<GUI::Action> m_open_selected_action;
- RefPtr<GUI::Action> m_add_existing_file_action;
RefPtr<GUI::Action> m_delete_action;
RefPtr<GUI::Action> m_switch_to_next_editor;
RefPtr<GUI::Action> m_switch_to_previous_editor;
diff --git a/DevTools/HackStudio/Project.cpp b/DevTools/HackStudio/Project.cpp
index 109aba8a22..2b8251bc78 100644
--- a/DevTools/HackStudio/Project.cpp
+++ b/DevTools/HackStudio/Project.cpp
@@ -26,368 +26,59 @@
#include "Project.h"
#include "HackStudio.h"
-#include <AK/LexicalPath.h>
-#include <AK/QuickSort.h>
-#include <AK/StringBuilder.h>
-#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
namespace HackStudio {
-struct Project::ProjectTreeNode : public RefCounted<ProjectTreeNode> {
- enum class Type {
- Invalid,
- Project,
- Directory,
- File,
- };
-
- ProjectTreeNode& find_or_create_subdirectory(const String& name)
- {
- for (auto& child : children) {
- if (child->type == Type::Directory && child->name == name)
- return *child;
- }
- auto new_child = adopt(*new ProjectTreeNode);
- new_child->type = Type::Directory;
- new_child->name = name;
- new_child->parent = this;
- auto* ptr = new_child.ptr();
- children.append(move(new_child));
- return *ptr;
- }
-
- void sort()
- {
- if (type == Type::File)
- return;
- quick_sort(children, [](auto& a, auto& b) {
- return a->name < b->name;
- });
- for (auto& child : children)
- child->sort();
- }
-
- Type type { Type::Invalid };
- String name;
- String path;
- Vector<NonnullRefPtr<ProjectTreeNode>> children;
- ProjectTreeNode* parent { nullptr };
-};
-
-class ProjectModel final : public GUI::Model {
-public:
- explicit ProjectModel(Project& project)
- : m_project(project)
- {
- }
-
- virtual int row_count(const GUI::ModelIndex& index) const override
- {
- if (!index.is_valid())
- return 1;
- auto* node = static_cast<Project::ProjectTreeNode*>(index.internal_data());
- return node->children.size();
- }
-
- virtual int column_count(const GUI::ModelIndex&) const override
- {
- return 1;
- }
-
- virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override
- {
- auto* node = static_cast<Project::ProjectTreeNode*>(index.internal_data());
- if (role == GUI::ModelRole::Display) {
- return node->name;
- }
- if (role == GUI::ModelRole::Custom) {
- return node->path;
- }
- if (role == GUI::ModelRole::Icon) {
- if (node->type == Project::ProjectTreeNode::Type::Project)
- return m_project.m_project_icon;
- if (node->type == Project::ProjectTreeNode::Type::Directory)
- return m_project.m_directory_icon;
- if (node->name.ends_with(".cpp"))
- return m_project.m_cplusplus_icon;
- if (node->name.ends_with(".frm"))
- return m_project.m_form_icon;
- if (node->name.ends_with(".h"))
- return m_project.m_header_icon;
- if (node->name.ends_with(".hsp"))
- return m_project.m_hackstudio_icon;
- if (node->name.ends_with(".js"))
- return m_project.m_javascript_icon;
- return m_project.m_file_icon;
- }
- if (role == GUI::ModelRole::Font) {
- if (node->name == currently_open_file())
- return Gfx::Font::default_bold_font();
- return {};
- }
- return {};
- }
-
- virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override
- {
- if (!parent.is_valid()) {
- return create_index(row, column, &m_project.root_node());
- }
- auto& node = *static_cast<Project::ProjectTreeNode*>(parent.internal_data());
- return create_index(row, column, node.children.at(row).ptr());
- }
-
- GUI::ModelIndex parent_index(const GUI::ModelIndex& index) const override
- {
- if (!index.is_valid())
- return {};
- auto& node = *static_cast<Project::ProjectTreeNode*>(index.internal_data());
- if (!node.parent)
- return {};
-
- if (!node.parent->parent) {
- return create_index(0, 0, &m_project.root_node());
- ASSERT_NOT_REACHED();
- return {};
- }
-
- for (size_t row = 0; row < node.parent->parent->children.size(); ++row) {
- if (node.parent->parent->children[row].ptr() == node.parent)
- return create_index(row, 0, node.parent);
- }
-
- ASSERT_NOT_REACHED();
- return {};
- }
-
- virtual void update() override
- {
- did_update();
- }
-
-private:
- Project& m_project;
-};
-
-Project::Project(const String& path, Vector<String>&& filenames)
- : m_path(path)
+Project::Project(const String& root_path)
+ : m_root_path(root_path)
{
- m_name = LexicalPath(m_path).basename();
-
- m_file_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png"));
- m_cplusplus_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-cplusplus.png"));
- m_header_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-header.png"));
- m_directory_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-folder.png"));
- m_project_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/hackstudio-project.png"));
- m_javascript_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-javascript.png"));
- m_hackstudio_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-hackstudio.png"));
- m_form_icon = GUI::Icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-form.png"));
-
- for (auto& filename : filenames) {
- m_files.append(ProjectFile::construct_with_name(filename));
- }
-
- m_model = adopt(*new ProjectModel(*this));
-
- rebuild_tree();
+ m_model = GUI::FileSystemModel::create(root_path, GUI::FileSystemModel::Mode::FilesAndDirectories);
}
Project::~Project()
{
}
-OwnPtr<Project> Project::load_from_file(const String& path)
+OwnPtr<Project> Project::open_with_root_path(const String& root_path)
{
- auto file = Core::File::construct(path);
- if (!file->open(Core::File::ReadOnly))
+ if (!Core::File::is_directory(root_path))
return nullptr;
-
- auto type = ProjectType::Cpp;
- Vector<String> files;
-
- auto add_glob = [&](String path) {
- auto split = path.split('*', true);
- for (auto& item : split) {
- dbg() << item;
- }
- ASSERT(split.size() == 2);
- auto cwd = getcwd(nullptr, 0);
- Core::DirIterator it(cwd, Core::DirIterator::Flags::SkipParentAndBaseDir);
- while (it.has_next()) {
- auto path = it.next_path();
- if (!split[0].is_empty() && !path.starts_with(split[0]))
- continue;
-
- if (!split[1].is_empty() && !path.ends_with(split[1]))
- continue;
-
- files.append(path);
- }
- };
-
- for (;;) {
- auto line = file->read_line(1024);
- if (line.is_null())
- break;
-
- auto path = String::copy(line, Chomp);
- if (path.contains("*"))
- add_glob(path);
- else
- files.append(path);
- }
-
- for (auto& file : files) {
- if (file.ends_with(".js")) {
- type = ProjectType::JavaScript;
- break;
- }
- }
-
- quick_sort(files);
-
- auto project = adopt_own(*new Project(path, move(files)));
- project->m_type = type;
- return project;
+ return adopt_own(*new Project(root_path));
}
-bool Project::add_file(const String& filename)
+template<typename Callback>
+static void traverse_model(const GUI::FileSystemModel& model, const GUI::ModelIndex& index, Callback callback)
{
- m_files.append(ProjectFile::construct_with_name(filename));
- rebuild_tree();
- m_model->update();
- return save();
-}
-
-bool Project::remove_file(const String& filename)
-{
- if (!get_file(filename))
- return false;
- m_files.remove_first_matching([filename](auto& file) { return file->name() == filename; });
- rebuild_tree();
- m_model->update();
- return save();
-}
-
-bool Project::save()
-{
- auto project_file = Core::File::construct(m_path);
- if (!project_file->open(Core::File::WriteOnly))
- return false;
-
- for (auto& file : m_files) {
- // FIXME: Check for error here. IODevice::printf() needs some work on error reporting.
- project_file->printf("%s\n", file.name().characters());
+ if (index.is_valid())
+ callback(index);
+ auto row_count = model.row_count(index);
+ if (!row_count)
+ return;
+ for (int row = 0; row < row_count; ++row) {
+ auto child_index = model.index(row, GUI::FileSystemModel::Column::Name, index);
+ traverse_model(model, child_index, callback);
}
-
- if (!project_file->close())
- return false;
-
- return true;
}
-RefPtr<ProjectFile> Project::get_file(const String& filename)
+void Project::for_each_text_file(Function<void(const ProjectFile&)> callback) const
{
- for (auto& file : m_files) {
- if (LexicalPath(file.name()).string() == LexicalPath(filename).string())
- return &file;
- }
- return nullptr;
-}
-
-String Project::default_file() const
-{
- if (m_files.size() > 0) {
- if (m_type != ProjectType::Unknown) {
- StringView extension;
- switch (m_type) {
- case ProjectType::Cpp:
- extension = ".cpp";
- break;
- case ProjectType::JavaScript:
- extension = ".js";
- break;
- default:
- ASSERT_NOT_REACHED();
- }
-
- auto project_file = m_files.find([&](auto project_file) {
- return project_file->name().ends_with(extension);
- });
-
- if (!project_file.is_end()) {
- auto& file = *project_file;
- return file->name();
- }
- }
-
- return m_files.first().name();
- }
-
- ASSERT_NOT_REACHED();
+ traverse_model(model(), {}, [&](auto& index) {
+ auto file = get_file(model().full_path(index));
+ if (file)
+ callback(*file);
+ });
}
-void Project::rebuild_tree()
+RefPtr<ProjectFile> Project::get_file(const String& path) const
{
- auto root = adopt(*new ProjectTreeNode);
- root->name = m_name;
- root->type = ProjectTreeNode::Type::Project;
-
for (auto& file : m_files) {
- LexicalPath path(file.name());
- ProjectTreeNode* current = root.ptr();
- StringBuilder partial_path;
-
- for (size_t i = 0; i < path.parts().size(); ++i) {
- auto& part = path.parts().at(i);
- if (part == ".")
- continue;
- if (i != path.parts().size() - 1) {
- current = &current->find_or_create_subdirectory(part);
- continue;
- }
- struct stat st;
- if (lstat(path.string().characters(), &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- current = &current->find_or_create_subdirectory(part);
- continue;
- }
- }
- auto file_node = adopt(*new ProjectTreeNode);
- file_node->name = part;
- file_node->path = path.string();
- file_node->type = Project::ProjectTreeNode::Type::File;
- file_node->parent = current;
- current->children.append(move(file_node));
- break;
- }
+ if (file.name() == path)
+ return file;
}
-
- root->sort();
-
-#if 0
- Function<void(ProjectTreeNode&, int indent)> dump_tree = [&](ProjectTreeNode& node, int indent) {
- for (int i = 0; i < indent; ++i)
- out(" ");
- if (node.name.is_null())
- outln("(null)");
- else
- outln("{}", node.name);
- for (auto& child : node.children) {
- dump_tree(*child, indent + 2);
- }
- };
-
- dump_tree(*root, 0);
-#endif
-
- m_root_node = move(root);
- m_model->update();
+ auto file = ProjectFile::construct_with_name(path);
+ m_files.append(file);
+ return file;
}
}
diff --git a/DevTools/HackStudio/Project.h b/DevTools/HackStudio/Project.h
index dddb34726f..6d3453e84e 100644
--- a/DevTools/HackStudio/Project.h
+++ b/DevTools/HackStudio/Project.h
@@ -29,19 +29,11 @@
#include "ProjectFile.h"
#include <AK/LexicalPath.h>
#include <AK/Noncopyable.h>
-#include <AK/NonnullRefPtrVector.h>
#include <AK/OwnPtr.h>
-#include <LibGUI/Icon.h>
-#include <LibGUI/Model.h>
+#include <LibGUI/FileSystemModel.h>
namespace HackStudio {
-enum class ProjectType {
- Unknown,
- Cpp,
- JavaScript
-};
-
class Project {
AK_MAKE_NONCOPYABLE(Project);
AK_MAKE_NONMOVABLE(Project);
@@ -49,52 +41,24 @@ class Project {
public:
~Project();
- static OwnPtr<Project> load_from_file(const String& path);
+ static OwnPtr<Project> open_with_root_path(const String& root_path);
- [[nodiscard]] bool add_file(const String& filename);
- [[nodiscard]] bool remove_file(const String& filename);
- [[nodiscard]] bool save();
+ GUI::FileSystemModel& model() { return *m_model; }
+ const GUI::FileSystemModel& model() const { return *m_model; }
+ String name() const { return LexicalPath(m_root_path).basename(); }
+ String root_path() const { return m_root_path; }
- RefPtr<ProjectFile> get_file(const String& filename);
+ RefPtr<ProjectFile> get_file(const String& path) const;
- ProjectType type() const { return m_type; }
- GUI::Model& model() { return *m_model; }
- String default_file() const;
- String name() const { return m_name; }
- String path() const { return m_path; }
- String root_directory() const { return LexicalPath(m_path).dirname(); }
-
- template<typename Callback>
- void for_each_text_file(Callback callback) const
- {
- for (auto& file : m_files) {
- callback(file);
- }
- }
+ void for_each_text_file(Function<void(const ProjectFile&)>) const;
private:
- friend class ProjectModel;
- struct ProjectTreeNode;
- explicit Project(const String& path, Vector<String>&& files);
-
- const ProjectTreeNode& root_node() const { return *m_root_node; }
- void rebuild_tree();
+ explicit Project(const String& root_path);
- ProjectType m_type { ProjectType::Unknown };
- String m_name;
- String m_path;
- RefPtr<GUI::Model> m_model;
- NonnullRefPtrVector<ProjectFile> m_files;
- RefPtr<ProjectTreeNode> m_root_node;
+ RefPtr<GUI::FileSystemModel> m_model;
+ mutable NonnullRefPtrVector<ProjectFile> m_files;
- GUI::Icon m_directory_icon;
- GUI::Icon m_file_icon;
- GUI::Icon m_cplusplus_icon;
- GUI::Icon m_header_icon;
- GUI::Icon m_project_icon;
- GUI::Icon m_javascript_icon;
- GUI::Icon m_hackstudio_icon;
- GUI::Icon m_form_icon;
+ String m_root_path;
};
}
diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp
index 7cad913aef..c6e1db8854 100644
--- a/DevTools/HackStudio/main.cpp
+++ b/DevTools/HackStudio/main.cpp
@@ -54,8 +54,6 @@ static RefPtr<HackStudioWidget> s_hack_studio_widget;
static bool make_is_available();
static void update_path_environment_variable();
-static String path_to_project(const String& path_argument_absolute_path);
-static void open_default_project_file(const String& project_path);
int main(int argc, char** argv)
{
@@ -73,7 +71,6 @@ int main(int argc, char** argv)
s_window = GUI::Window::construct();
s_window->resize(840, 600);
- s_window->set_title("HackStudio");
s_window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-hack-studio.png"));
update_path_environment_variable();
@@ -88,16 +85,20 @@ int main(int argc, char** argv)
auto argument_absolute_path = Core::File::real_path_for(path_argument);
- auto menubar = GUI::MenuBar::construct();
- auto project_path = path_to_project(argument_absolute_path);
+ auto project_path = argument_absolute_path;
+ if (argument_absolute_path.is_null())
+ project_path = Core::File::real_path_for(".");
+
s_hack_studio_widget = s_window->set_main_widget<HackStudioWidget>(project_path);
+ s_window->set_title(String::formatted("{} - HackStudio", s_hack_studio_widget->project().name()));
+
+ auto menubar = GUI::MenuBar::construct();
s_hack_studio_widget->initialize_menubar(menubar);
app->set_menubar(menubar);
s_window->show();
- open_default_project_file(argument_absolute_path);
s_hack_studio_widget->update_actions();
return app->exec();
@@ -131,22 +132,6 @@ static void update_path_environment_variable()
setenv("PATH", path.to_string().characters(), true);
}
-static String path_to_project(const String& path_argument_absolute_path)
-{
- if (path_argument_absolute_path.ends_with(".hsp"))
- return path_argument_absolute_path;
- else
- return "/home/anon/Source/little/little.hsp";
-}
-
-static void open_default_project_file(const String& project_path)
-{
- if (!project_path.is_empty() && !project_path.ends_with(".hsp"))
- open_file(project_path);
- else
- open_file(s_hack_studio_widget->project().default_file());
-}
-
namespace HackStudio {
GUI::TextEditor& current_editor()