diff --git a/src/prodir/cpp/tree_structurer.cpp b/src/prodir/cpp/tree_structurer.cpp index 2302cce..fe31f25 100644 --- a/src/prodir/cpp/tree_structurer.cpp +++ b/src/prodir/cpp/tree_structurer.cpp @@ -1,5 +1,5 @@ #include "tree_structurer.hpp" - +#include #include #include #include @@ -123,62 +123,166 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) std::vector TreeStructurer::get_directory_structure(const std::string& startpath) { std::vector result; - // Normalize the input path by removing ./ or .\ prefix if present std::string normalized_path = startpath; - if (startpath.substr(0, 2) == ".\\" || startpath.substr(0, 2) == "./") { - normalized_path = startpath.substr(2); + if (normalized_path.size() >= 2 && + (normalized_path.substr(0, 2) == ".\\" || normalized_path.substr(0, 2) == "./")) { + normalized_path = normalized_path.substr(2); } - fs::path start = normalized_path.empty() ? fs::current_path() : fs::path(normalized_path); - + try { - auto paths = get_filtered_paths(start); - if (paths.empty()) { - throw std::runtime_error("No valid files or directories found in: " + start.string()); + if (!fs::exists(start)) { + throw std::runtime_error("Directory does not exist: " + start.string()); } - std::vector is_last_at_level(256, false); - for (size_t i = 1; i < paths.size(); ++i) { - const auto& path = paths[i]; + if (!fs::is_directory(start)) { + throw std::runtime_error("Path is not a directory: " + start.string()); + } + + // Start with the root node + std::string root_name = start.filename().string(); + result.push_back(root_name + "/"); + + // First, collect all paths and organize them by their parent directories + std::map> dir_contents; + std::vector dir_paths; // To maintain order of directories + + // Add root directory + dir_contents["/"] = {}; + dir_paths.push_back("/"); + + // Collect files and directories + for (const auto& entry : fs::recursive_directory_iterator(start)) { + const auto& path = entry.path(); std::string rel_path = get_relative_path(path, start); - int level = std::count(rel_path.begin(), rel_path.end(), fs::path::preferred_separator); - - bool is_last = true; - for (size_t j = i + 1; j < paths.size(); ++j) { - std::string next_rel_path = get_relative_path(paths[j], start); - int next_level = std::count(next_rel_path.begin(), next_rel_path.end(), fs::path::preferred_separator); - if (next_level == level) { - is_last = false; - break; - } - if (next_level < level) { + // Replace backslashes with forward slashes + std::replace(rel_path.begin(), rel_path.end(), '\\', '/'); + + // Skip if it should be ignored + bool should_skip = false; + for (const auto& component : path) { + if (should_ignore_dir(component.string())) { + should_skip = true; break; } } - - is_last_at_level[level] = is_last; - - std::string line; - for (int j = 0; j < level; ++j) { - if (j == level - 1) { - line += is_last ? "└── " : "├── "; - } else { - line += is_last_at_level[j] ? " " : "│ "; + if (should_skip) continue; + + if (entry.is_directory()) { + if (should_ignore_dir(path.filename().string())) { + continue; } + + // Add directory to its parent + std::string parent_path = "/"; + size_t last_slash = rel_path.find_last_of('/'); + if (last_slash != std::string::npos) { + parent_path = rel_path.substr(0, last_slash); + if (parent_path.empty()) { + parent_path = "/"; + } + } + + // Create parent directory entry if it doesn't exist + if (dir_contents.find(parent_path) == dir_contents.end()) { + dir_contents[parent_path] = {}; + dir_paths.push_back(parent_path); + } + + // Create directory entry + dir_contents[parent_path].push_back(path); + + // Create entry for this directory's contents + dir_contents[rel_path] = {}; + dir_paths.push_back(rel_path); + } else { + if (should_ignore_file(path.filename().string())) { + continue; + } + + // Add file to its parent + std::string parent_path = "/"; + size_t last_slash = rel_path.find_last_of('/'); + if (last_slash != std::string::npos) { + parent_path = rel_path.substr(0, last_slash); + if (parent_path.empty()) { + parent_path = "/"; + } + } + + // Create parent directory entry if it doesn't exist + if (dir_contents.find(parent_path) == dir_contents.end()) { + dir_contents[parent_path] = {}; + dir_paths.push_back(parent_path); + } + + dir_contents[parent_path].push_back(path); } - - line += path.filename().string(); - if (fs::is_directory(path)) { - line += "/"; - } - result.push_back(line); } + + // Now create the tree structure + std::function&)> + build_tree = [&](const std::string& dir_path, const std::string& prefix, std::vector& is_last_stack) { + // Sort paths - directories first, then files + std::vector& paths = dir_contents[dir_path]; + std::sort(paths.begin(), paths.end(), [](const fs::path& a, const fs::path& b) { + bool a_is_dir = fs::is_directory(a); + bool b_is_dir = fs::is_directory(b); + if (a_is_dir != b_is_dir) { + return a_is_dir > b_is_dir; // Directories before files + } + return a.filename() < b.filename(); // Alphabetical order + }); + + // Add entries for this directory + for (size_t i = 0; i < paths.size(); ++i) { + const auto& path = paths[i]; + bool is_last = (i == paths.size() - 1); + + // Build the line prefix + std::string line_prefix = prefix; + if (!prefix.empty()) { + line_prefix += is_last ? "└── " : "├── "; + } + + // Add the file/directory name + std::string name = path.filename().string(); + std::string line = line_prefix + name; + + if (fs::is_directory(path)) { + line += "/"; + } + + result.push_back(line); + + // If it's a directory, process its contents with updated prefix + if (fs::is_directory(path)) { + std::string rel_path = get_relative_path(path, start); + std::replace(rel_path.begin(), rel_path.end(), '\\', '/'); + + std::string next_prefix = prefix; + if (!prefix.empty()) { + next_prefix += is_last ? " " : "│ "; + } + + std::vector next_is_last_stack = is_last_stack; + next_is_last_stack.push_back(is_last); + + build_tree(rel_path, next_prefix, next_is_last_stack); + } + } + }; + + // Start building the tree from the root + std::vector is_last_stack; + build_tree("/", "", is_last_stack); + } catch (const fs::filesystem_error& e) { throw std::runtime_error("Failed to access directory: " + std::string(e.what())); } - + return result; } @@ -406,7 +510,7 @@ void TreeStructurer::create_file(const fs::path& path) { } } -// Reads a structure file into a vector of non-empty lines. +// Reads a structure file into a vector of non-empty lines, ignoring comments. std::vector TreeStructurer::read_structure_file(const std::string& filepath) { std::vector lines; // Open file in binary mode to avoid Windows CRLF conversion @@ -416,16 +520,45 @@ std::vector TreeStructurer::read_structure_file(const std::string& } std::string line; + while (std::getline(file, line)) { // Remove carriage return if present (Windows files) if (!line.empty() && line.back() == '\r') { line.pop_back(); } - + + std::cout << "Processing line: " << line << std::endl; + + size_t hash_pos = line.find('#'); + size_t single_line_comment_pos = line.find("//"); + size_t multi_line_comment_start_pos = line.find("/*"); + + if (hash_pos != std::string::npos) { + // Trim the line at the hash comment + line = line.substr(0, hash_pos); + std::cout << "Trimmed line at hash comment position: " << hash_pos << std::endl; + } else if (single_line_comment_pos != std::string::npos) { + // Trim the line at the single-line comment + line = line.substr(0, single_line_comment_pos); + std::cout << "Trimmed line at single-line comment position: " << single_line_comment_pos << std::endl; + } else if (multi_line_comment_start_pos != std::string::npos) { + // Trim the line at the multi-line comment start + line = line.substr(0, multi_line_comment_start_pos); + std::cout << "Trimmed line at multi-line comment start position: " << multi_line_comment_start_pos << std::endl; + } + + // Remove leading and trailing whitespace + line.erase(0, line.find_first_not_of(" \t\n\r\f\v")); + line.erase(line.find_last_not_of(" \t\n\r\f\v") + 1); + if (!line.empty()) { lines.push_back(line); + std::cout << "Added non-empty line: " << line << std::endl; + } else { + std::cout << "Skipped empty or commented-out line." << std::endl; } } + return lines; } // Checks the structure for obvious mistakes (e.g. a jump in indentation).