diff options
Diffstat (limited to 'DevTools')
-rw-r--r-- | DevTools/HackStudio/Editor.cpp | 9 | ||||
-rw-r--r-- | DevTools/HackStudio/HackStudioWidget.cpp | 80 | ||||
-rw-r--r-- | DevTools/HackStudio/HackStudioWidget.h | 5 | ||||
-rw-r--r-- | DevTools/HackStudio/Project.cpp | 365 | ||||
-rw-r--r-- | DevTools/HackStudio/Project.h | 60 | ||||
-rw-r--r-- | DevTools/HackStudio/main.cpp | 29 |
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 = ¤t->find_or_create_subdirectory(part); - continue; - } - struct stat st; - if (lstat(path.string().characters(), &st) == 0) { - if (S_ISDIR(st.st_mode)) { - current = ¤t->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() |