From 13ce480fac2c9d00cdb7c07b1461ce4276a4ed8a Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 12:14:38 +0100 Subject: [PATCH 01/10] vs code setup --- .vscode/c_cpp_properties.json | 19 +++++++++ .vscode/settings.json | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..774906b --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,19 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "C:/Users/Falko/AppData/Local/Programs/Python/Python311/include", + "C:/Users/Falko/AppData/Local/Programs/Python/Python311/Lib/site-packages/pybind11/include" + ], + "defines": [], + "windowsSdkVersion": "10.0.19041.0", + "compilerPath": "cl.exe", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "windows-msvc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1c29706 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,72 @@ +{ + "files.associations": { + "*.py": "python", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "filesystem": "cpp", + "format": "cpp", + "forward_list": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xutility": "cpp" + } +} \ No newline at end of file From d26b956c157775265ce89104bec4499b8313d4d5 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 12:14:53 +0100 Subject: [PATCH 02/10] python code --- setup.py | 27 +++++++++++++++++++++++++++ tree_structurer/__init__.py | 1 + tree_structurer/__main__.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 setup.py create mode 100644 tree_structurer/__init__.py create mode 100644 tree_structurer/__main__.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2b98270 --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +from setuptools import setup, Extension +import platform + +extra_compile_args = [] +extra_link_args = [] + +# Set C++17 flag based on the compiler +if platform.system() == "Windows": + extra_compile_args.append('/std:c++17') +else: + extra_compile_args.append('-std=c++17') + +tree_structurer_module = Extension( + 'tree_structurer._tree_structurer', + sources=['tree_structurer/cpp/bindings.cpp', 'tree_structurer/cpp/tree_structurer.cpp'], + include_dirs=['tree_structurer/cpp'], + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, +) + +setup( + name='tree_structurer', + version='0.0.2', + description='A module for analyzing directory structures', + ext_modules=[tree_structurer_module], + packages=['tree_structurer'], +) \ No newline at end of file diff --git a/tree_structurer/__init__.py b/tree_structurer/__init__.py new file mode 100644 index 0000000..d99440a --- /dev/null +++ b/tree_structurer/__init__.py @@ -0,0 +1 @@ +from tree_structurer._tree_structurer import create_tree_structurer, get_structure \ No newline at end of file diff --git a/tree_structurer/__main__.py b/tree_structurer/__main__.py new file mode 100644 index 0000000..3407a6e --- /dev/null +++ b/tree_structurer/__main__.py @@ -0,0 +1,31 @@ +import os +import sys +import argparse +from tree_structurer import create_tree_structurer, get_structure + +def main(): + parser = argparse.ArgumentParser(description='Display directory structure in a tree-like format') + parser.add_argument('path', nargs='?', default=os.getcwd(), + help='Path to analyze (default: current directory)') + parser.add_argument('-v', '--verbose', action='store_true', + help='Show more detailed output') + args = parser.parse_args() + + try: + if args.verbose: + print(f"Analyzing directory: {args.path}") + + # Create tree structurer instance + create_tree_structurer() + + # Get and print structure + structure = get_structure(args.path) + for line in structure: + print(line) + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file From 94eda933069c4a7fbca0afab6c86d8d3936fe4da Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 12:15:03 +0100 Subject: [PATCH 03/10] basic c++ code --- tree_structurer/cpp/bindings.cpp | 72 +++++++++++++++++++++++++ tree_structurer/cpp/tree_structurer.cpp | 60 +++++++++++++++++++++ tree_structurer/cpp/tree_structurer.hpp | 16 ++++++ 3 files changed, 148 insertions(+) create mode 100644 tree_structurer/cpp/bindings.cpp create mode 100644 tree_structurer/cpp/tree_structurer.cpp create mode 100644 tree_structurer/cpp/tree_structurer.hpp diff --git a/tree_structurer/cpp/bindings.cpp b/tree_structurer/cpp/bindings.cpp new file mode 100644 index 0000000..6aa031a --- /dev/null +++ b/tree_structurer/cpp/bindings.cpp @@ -0,0 +1,72 @@ +#define PY_SSIZE_T_CLEAN +#include +#include "tree_structurer.hpp" + +static PyObject* TreeStructurerError; +static TreeStructurer* g_tree_structurer = nullptr; + +static PyObject* create_tree_structurer(PyObject* self, PyObject* args) { + if (g_tree_structurer != nullptr) { + delete g_tree_structurer; + } + g_tree_structurer = new TreeStructurer(); + Py_RETURN_NONE; +} + +static PyObject* get_structure(PyObject* self, PyObject* args) { + const char* path = nullptr; + if (!PyArg_ParseTuple(args, "|s", &path)) { + return NULL; + } + + if (g_tree_structurer == nullptr) { + PyErr_SetString(TreeStructurerError, "TreeStructurer not initialized"); + return NULL; + } + + try { + std::string path_str = path ? path : ""; + std::vector structure = g_tree_structurer->get_directory_structure(path_str); + PyObject* list = PyList_New(structure.size()); + for (size_t i = 0; i < structure.size(); i++) { + PyList_SET_ITEM(list, i, PyUnicode_FromString(structure[i].c_str())); + } + return list; + } catch (const std::exception& e) { + PyErr_SetString(TreeStructurerError, e.what()); + return NULL; + } +} + +static PyMethodDef TreeStructurerMethods[] = { + {"create_tree_structurer", create_tree_structurer, METH_NOARGS, + "Create a new TreeStructurer instance"}, + {"get_structure", get_structure, METH_VARARGS, + "Get the directory structure for the given path"}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef tree_structurer_module = { + PyModuleDef_HEAD_INIT, + "_tree_structurer", // Changed module name to match Python import + "Module for analyzing directory structures", + -1, + TreeStructurerMethods +}; + +PyMODINIT_FUNC PyInit__tree_structurer(void) { // Changed function name to match module name + PyObject* m = PyModule_Create(&tree_structurer_module); + if (m == NULL) + return NULL; + + TreeStructurerError = PyErr_NewException("tree_structurer.error", NULL, NULL); + Py_XINCREF(TreeStructurerError); + if (PyModule_AddObject(m, "error", TreeStructurerError) < 0) { + Py_XDECREF(TreeStructurerError); + Py_CLEAR(TreeStructurerError); + Py_DECREF(m); + return NULL; + } + + return m; +} \ No newline at end of file diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp new file mode 100644 index 0000000..219455f --- /dev/null +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -0,0 +1,60 @@ +#include "tree_structurer.hpp" +#include +#include + +namespace fs = std::filesystem; + +bool TreeStructurer::should_ignore_dir(const std::string& dirname) { + return dirname[0] == '.' || + dirname.find("build") != std::string::npos || + dirname == "venv" || + dirname == "myenv"; +} + +bool TreeStructurer::should_ignore_file(const std::string& filename) { + return filename[0] == '.' || + filename.find("build") != std::string::npos; +} + +std::string TreeStructurer::create_indent(int level) { + return std::string(4 * level, ' '); +} + +std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::path& base) { + const fs::path rel = fs::relative(path, base); + return rel.string(); +} + +std::vector TreeStructurer::get_directory_structure(const std::string& startpath) { + std::vector result; + fs::path start = startpath.empty() ? fs::current_path() : fs::path(startpath); + + try { + std::vector paths; + for(const auto& entry : fs::recursive_directory_iterator(start)) { + paths.push_back(entry.path()); + } + + // Sort paths for consistent output + std::sort(paths.begin(), paths.end()); + + for(const auto& path : paths) { + std::string rel_path = get_relative_path(path, start); + int level = std::count(rel_path.begin(), rel_path.end(), fs::path::preferred_separator); + + if(fs::is_directory(path)) { + if(!should_ignore_dir(path.filename().string())) { + result.push_back(create_indent(level) + path.filename().string() + "/"); + } + } else { + if(!should_ignore_file(path.filename().string())) { + result.push_back(create_indent(level) + path.filename().string()); + } + } + } + } catch (const fs::filesystem_error& e) { + throw std::runtime_error("Failed to access directory: " + std::string(e.what())); + } + + return result; +} \ No newline at end of file diff --git a/tree_structurer/cpp/tree_structurer.hpp b/tree_structurer/cpp/tree_structurer.hpp new file mode 100644 index 0000000..102bf26 --- /dev/null +++ b/tree_structurer/cpp/tree_structurer.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include + +class TreeStructurer { +public: + TreeStructurer() = default; + std::vector get_directory_structure(const std::string& startpath = std::filesystem::current_path().string()); + +private: + bool should_ignore_dir(const std::string& dirname); + bool should_ignore_file(const std::string& filename); + std::string create_indent(int level); + std::string get_relative_path(const std::filesystem::path& path, const std::filesystem::path& base); +}; \ No newline at end of file From ee3dc7da5c4fdea2a31575e518167cf1826369af Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 15:24:17 +0100 Subject: [PATCH 04/10] correctly detecting build as a dir. --- tree_structurer/cpp/tree_structurer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index 219455f..933012b 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -5,15 +5,16 @@ namespace fs = std::filesystem; bool TreeStructurer::should_ignore_dir(const std::string& dirname) { - return dirname[0] == '.' || + return dirname[0] == '.' || + dirname[0] == '_'|| dirname.find("build") != std::string::npos || dirname == "venv" || - dirname == "myenv"; + dirname == "myenv" || + dirname == "build"; } bool TreeStructurer::should_ignore_file(const std::string& filename) { - return filename[0] == '.' || - filename.find("build") != std::string::npos; + return filename[0] == '.'; } std::string TreeStructurer::create_indent(int level) { From 44aaf183e741469e3e999c01ed61b52788bffcee Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 15:50:22 +0100 Subject: [PATCH 05/10] updated version to improve the code a bit --- example.py | 3 + setup.py | 2 +- tree_structurer/__main__.py | 5 +- tree_structurer/cpp/tree_structurer.cpp | 107 +++++++++++++++++++----- tree_structurer/cpp/tree_structurer.hpp | 1 + 5 files changed, 90 insertions(+), 28 deletions(-) create mode 100644 example.py diff --git a/example.py b/example.py new file mode 100644 index 0000000..c0e7345 --- /dev/null +++ b/example.py @@ -0,0 +1,3 @@ +import tree_structurer + +tree_structurer.get_structure() \ No newline at end of file diff --git a/setup.py b/setup.py index 2b98270..264db61 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ tree_structurer_module = Extension( setup( name='tree_structurer', - version='0.0.2', + version='0.0.4', description='A module for analyzing directory structures', ext_modules=[tree_structurer_module], packages=['tree_structurer'], diff --git a/tree_structurer/__main__.py b/tree_structurer/__main__.py index 3407a6e..cb5c6d4 100644 --- a/tree_structurer/__main__.py +++ b/tree_structurer/__main__.py @@ -14,11 +14,8 @@ def main(): try: if args.verbose: print(f"Analyzing directory: {args.path}") - - # Create tree structurer instance create_tree_structurer() - - # Get and print structure + # Get and print structure directly structure = get_structure(args.path) for line in structure: print(line) diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index 933012b..028cdb0 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -5,24 +5,74 @@ namespace fs = std::filesystem; bool TreeStructurer::should_ignore_dir(const std::string& dirname) { - return dirname[0] == '.' || - dirname[0] == '_'|| - dirname.find("build") != std::string::npos || - dirname == "venv" || - dirname == "myenv" || - dirname == "build"; + static const std::vector ignore_list = { + "build", "venv", "myenv", "dist", "node_modules", "CMakeFiles", + ".git", ".idea", ".vscode", "__pycache__" + }; + + return dirname[0] == '.' || + dirname[0] == '_' || // This will catch __pycache__ and _directories + std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end(); } bool TreeStructurer::should_ignore_file(const std::string& filename) { - return filename[0] == '.'; + static const std::vector ignore_extensions = { + ".pyc", ".pyo", ".pyd", ".so", ".dll", ".dylib", + ".o", ".obj", ".a", ".lib" + }; + + if (filename[0] == '.') return true; + + fs::path path(filename); + std::string ext = path.extension().string(); + return std::find(ignore_extensions.begin(), ignore_extensions.end(), ext) != ignore_extensions.end(); +} + +std::vector TreeStructurer::get_filtered_paths(const fs::path& start) { + std::vector paths; + + for(const auto& entry : fs::recursive_directory_iterator(start)) { + const auto& path = entry.path(); + bool should_include = true; + + for(auto it = path.begin(); it != path.end(); ++it) { + if(should_ignore_dir(it->string())) { + should_include = false; + break; + } + } + + if(should_include) { + if(fs::is_directory(path)) { + if(!should_ignore_dir(path.filename().string())) { + paths.push_back(path); + } + } else { + if(!should_ignore_file(path.filename().string())) { + paths.push_back(path); + } +} + } + } + + std::sort(paths.begin(), paths.end()); + return paths; } std::string TreeStructurer::create_indent(int level) { - return std::string(4 * level, ' '); + if (level == 0) return ""; + + std::string indent; + for (int i = 0; i < level - 1; ++i) { + indent += "│ "; + } + indent += "├─ "; + return indent; } + std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::path& base) { - const fs::path rel = fs::relative(path, base); + fs::path rel = fs::relative(path, base); return rel.string(); } @@ -31,27 +81,38 @@ std::vector TreeStructurer::get_directory_structure(const std::stri fs::path start = startpath.empty() ? fs::current_path() : fs::path(startpath); try { - std::vector paths; - for(const auto& entry : fs::recursive_directory_iterator(start)) { - paths.push_back(entry.path()); - } + result.push_back(start.filename().string() + "/"); - // Sort paths for consistent output - std::sort(paths.begin(), paths.end()); + auto paths = get_filtered_paths(start); + std::vector is_last_at_level(256, false); - for(const auto& path : paths) { + for (size_t i = 0; i < paths.size(); ++i) { + const auto& path = paths[i]; std::string rel_path = get_relative_path(path, start); int level = std::count(rel_path.begin(), rel_path.end(), fs::path::preferred_separator); - if(fs::is_directory(path)) { - if(!should_ignore_dir(path.filename().string())) { - result.push_back(create_indent(level) + path.filename().string() + "/"); - } - } else { - if(!should_ignore_file(path.filename().string())) { - result.push_back(create_indent(level) + path.filename().string()); + bool is_last = (i == paths.size() - 1) || + (i < paths.size() - 1 && + std::count(get_relative_path(paths[i + 1], start).begin(), + get_relative_path(paths[i + 1], start).end(), + fs::path::preferred_separator) <= level); + + 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] ? " " : "│ "; } } + + line += path.filename().string(); + if(fs::is_directory(path)) { + line += "/"; + } + result.push_back(line); } } catch (const fs::filesystem_error& e) { throw std::runtime_error("Failed to access directory: " + std::string(e.what())); diff --git a/tree_structurer/cpp/tree_structurer.hpp b/tree_structurer/cpp/tree_structurer.hpp index 102bf26..06c51be 100644 --- a/tree_structurer/cpp/tree_structurer.hpp +++ b/tree_structurer/cpp/tree_structurer.hpp @@ -13,4 +13,5 @@ private: bool should_ignore_file(const std::string& filename); std::string create_indent(int level); std::string get_relative_path(const std::filesystem::path& path, const std::filesystem::path& base); + std::vector get_filtered_paths(const std::filesystem::path& start); }; \ No newline at end of file From 6d5610c05ce7937e3a1821c1c3e0966766f79d46 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 15:54:03 +0100 Subject: [PATCH 06/10] nothing fixed --- tree_structurer/cpp/tree_structurer.cpp | 79 ++++++++++++++++--------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index 028cdb0..a2af3cb 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -3,16 +3,23 @@ #include namespace fs = std::filesystem; - bool TreeStructurer::should_ignore_dir(const std::string& dirname) { static const std::vector ignore_list = { "build", "venv", "myenv", "dist", "node_modules", "CMakeFiles", ".git", ".idea", ".vscode", "__pycache__" }; - - return dirname[0] == '.' || - dirname[0] == '_' || // This will catch __pycache__ and _directories - std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end(); + + // Ignore directories in the ignore list + if (std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end()) { + return true; + } + + // Ignore directories starting with '.' or '_' + if (!dirname.empty() && (dirname[0] == '.' || dirname[0] == '_')) { + return true; + } + + return false; } bool TreeStructurer::should_ignore_file(const std::string& filename) { @@ -20,45 +27,61 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) { ".pyc", ".pyo", ".pyd", ".so", ".dll", ".dylib", ".o", ".obj", ".a", ".lib" }; - - if (filename[0] == '.') return true; - + + // Ignore files starting with '.' or '_' + if (!filename.empty() && (filename[0] == '.' || filename[0] == '_')) { + return true; + } + + // Ignore files with specific extensions fs::path path(filename); std::string ext = path.extension().string(); - return std::find(ignore_extensions.begin(), ignore_extensions.end(), ext) != ignore_extensions.end(); + if (std::find(ignore_extensions.begin(), ignore_extensions.end(), ext) != ignore_extensions.end()) { + return true; + } + + return false; } std::vector TreeStructurer::get_filtered_paths(const fs::path& start) { - std::vector paths; - - for(const auto& entry : fs::recursive_directory_iterator(start)) { + std::vector paths; + + for (const auto& entry : fs::recursive_directory_iterator(start)) { const auto& path = entry.path(); - bool should_include = true; - - for(auto it = path.begin(); it != path.end(); ++it) { - if(should_ignore_dir(it->string())) { - should_include = false; + + // Skip any path that contains an ignored directory + bool contains_ignored_dir = false; + for (auto it = path.begin(); it != path.end(); ++it) { + if (should_ignore_dir(it->string())) { + contains_ignored_dir = true; break; + } } + + if (contains_ignored_dir) { + continue; } - - if(should_include) { - if(fs::is_directory(path)) { - if(!should_ignore_dir(path.filename().string())) { - paths.push_back(path); + + // Handle directories + if (fs::is_directory(path)) { + if (!should_ignore_dir(path.filename().string())) { + paths.push_back(path); + } } - } else { - if(!should_ignore_file(path.filename().string())) { - paths.push_back(path); - } -} + // Handle files + else { + if (!should_ignore_file(path.filename().string())) { + paths.push_back(path); + } } } - + std::sort(paths.begin(), paths.end()); return paths; } + + std::string TreeStructurer::create_indent(int level) { if (level == 0) return ""; From 06bed02427d367d8f7d572637d2ceaab36469db9 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 20:04:40 +0100 Subject: [PATCH 07/10] tried improvement --- setup.py | 2 +- tree_structurer/__main__.py | 1 + tree_structurer/cpp/tree_structurer.cpp | 130 +++++++++++++----------- 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/setup.py b/setup.py index 264db61..576a701 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ tree_structurer_module = Extension( setup( name='tree_structurer', - version='0.0.4', + version='0.0.5', description='A module for analyzing directory structures', ext_modules=[tree_structurer_module], packages=['tree_structurer'], diff --git a/tree_structurer/__main__.py b/tree_structurer/__main__.py index cb5c6d4..ba5c985 100644 --- a/tree_structurer/__main__.py +++ b/tree_structurer/__main__.py @@ -17,6 +17,7 @@ def main(): create_tree_structurer() # Get and print structure directly structure = get_structure(args.path) + print(f"dir Structure for {args.path}:") for line in structure: print(line) diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index a2af3cb..db3834d 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -3,18 +3,17 @@ #include namespace fs = std::filesystem; + bool TreeStructurer::should_ignore_dir(const std::string& dirname) { static const std::vector ignore_list = { "build", "venv", "myenv", "dist", "node_modules", "CMakeFiles", ".git", ".idea", ".vscode", "__pycache__" }; - // Ignore directories in the ignore list if (std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end()) { return true; } - // Ignore directories starting with '.' or '_' if (!dirname.empty() && (dirname[0] == '.' || dirname[0] == '_')) { return true; } @@ -28,12 +27,10 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) { ".o", ".obj", ".a", ".lib" }; - // Ignore files starting with '.' or '_' if (!filename.empty() && (filename[0] == '.' || filename[0] == '_')) { return true; } - // Ignore files with specific extensions fs::path path(filename); std::string ext = path.extension().string(); if (std::find(ignore_extensions.begin(), ignore_extensions.end(), ext) != ignore_extensions.end()) { @@ -45,55 +42,44 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) { std::vector TreeStructurer::get_filtered_paths(const fs::path& start) { std::vector paths; + fs::directory_options options = fs::directory_options::skip_permission_denied; - for (const auto& entry : fs::recursive_directory_iterator(start)) { - const auto& path = entry.path(); + try { + if (fs::exists(start) && fs::is_directory(start)) { + paths.push_back(start); - // Skip any path that contains an ignored directory - bool contains_ignored_dir = false; - for (auto it = path.begin(); it != path.end(); ++it) { - if (should_ignore_dir(it->string())) { - contains_ignored_dir = true; - break; - } - } - - if (contains_ignored_dir) { - continue; - } - - // Handle directories - if (fs::is_directory(path)) { - if (!should_ignore_dir(path.filename().string())) { - paths.push_back(path); - } - } - // Handle files - else { - if (!should_ignore_file(path.filename().string())) { - paths.push_back(path); + for (const auto& entry : fs::recursive_directory_iterator(start, options)) { + const auto& path = entry.path(); + + // Check if any parent directory should be ignored + bool should_skip = false; + for (const auto& component : path) { + if (should_ignore_dir(component.string())) { + should_skip = true; + break; + } + } + if (should_skip) continue; + + if (entry.is_directory()) { + if (should_ignore_dir(path.filename().string())) { + paths.push_back(path); + } + } else { + if (should_ignore_file(path.filename().string())) { + paths.push_back(path); + } + } } } + } catch (const fs::filesystem_error& e) { + throw std::runtime_error("Error accessing path: " + std::string(e.what())); } std::sort(paths.begin(), paths.end()); return paths; } - - -std::string TreeStructurer::create_indent(int level) { - if (level == 0) return ""; - - std::string indent; - for (int i = 0; i < level - 1; ++i) { - indent += "│ "; - } - indent += "├─ "; - return indent; -} - - std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::path& base) { fs::path rel = fs::relative(path, base); return rel.string(); @@ -102,26 +88,50 @@ std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::pa std::vector TreeStructurer::get_directory_structure(const std::string& startpath) { std::vector result; fs::path start = startpath.empty() ? fs::current_path() : fs::path(startpath); - + try { - result.push_back(start.filename().string() + "/"); - auto paths = get_filtered_paths(start); + if (paths.empty()) return result; + + // Don't add the root directory name if we're in the current directory + if (start != fs::current_path()) { + result.push_back(start.filename().string()); + } + std::vector is_last_at_level(256, false); - - for (size_t i = 0; i < paths.size(); ++i) { + std::string last_prefix; + + for (size_t i = 1; i < paths.size(); ++i) { const auto& path = paths[i]; std::string rel_path = get_relative_path(path, start); + + // Split the path into components and check each component + bool should_skip = false; + for (const auto& component : fs::path(rel_path)) { + if (should_ignore_dir(component.string())) { + should_skip = true; + break; + } + } + if (should_skip) continue; + int level = std::count(rel_path.begin(), rel_path.end(), fs::path::preferred_separator); - - bool is_last = (i == paths.size() - 1) || - (i < paths.size() - 1 && - std::count(get_relative_path(paths[i + 1], start).begin(), - get_relative_path(paths[i + 1], start).end(), - fs::path::preferred_separator) <= level); - + + // Check if this is the last item at this level + bool is_last = false; + for (size_t j = i + 1; j < paths.size(); j++) { + const auto& next_path = paths[j]; + std::string next_rel_path = get_relative_path(next_path, start); + int next_level = std::count(next_rel_path.begin(), next_rel_path.end(), fs::path::preferred_separator); + if (next_level <= level) { + is_last = (j == paths.size() - 1) || (next_level < level); + break; + } + } + if (i == paths.size() - 1) is_last = true; + is_last_at_level[level] = is_last; - + std::string line; for (int j = 0; j < level; ++j) { if (j == level - 1) { @@ -130,9 +140,9 @@ std::vector TreeStructurer::get_directory_structure(const std::stri line += is_last_at_level[j] ? " " : "│ "; } } - + line += path.filename().string(); - if(fs::is_directory(path)) { + if (fs::is_directory(path)) { line += "/"; } result.push_back(line); @@ -140,6 +150,6 @@ std::vector TreeStructurer::get_directory_structure(const std::stri } catch (const fs::filesystem_error& e) { throw std::runtime_error("Failed to access directory: " + std::string(e.what())); } - + return result; -} \ No newline at end of file +} From fb66c6f510dae62dae11afe50e5e55b791b3768a Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 22:16:12 +0100 Subject: [PATCH 08/10] BUGFIX for getting correct files --- tree_structurer/cpp/tree_structurer.cpp | 79 +++++++++++++------------ 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index db3834d..23ff0db 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -7,20 +7,26 @@ namespace fs = std::filesystem; bool TreeStructurer::should_ignore_dir(const std::string& dirname) { static const std::vector ignore_list = { "build", "venv", "myenv", "dist", "node_modules", "CMakeFiles", - ".git", ".idea", ".vscode", "__pycache__" + ".git", ".idea", ".vscode", "__pycache__", "**pycache**" }; if (std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end()) { return true; } - if (!dirname.empty() && (dirname[0] == '.' || dirname[0] == '_')) { - return true; + if (!dirname.empty()) { + if (dirname[0] == '.' || dirname[0] == '_') { + return true; + } + if (dirname.find("__") == 0 && dirname.find("__", 2) != std::string::npos) { + return true; + } } return false; } +// Add the missing should_ignore_file implementation bool TreeStructurer::should_ignore_file(const std::string& filename) { static const std::vector ignore_extensions = { ".pyc", ".pyo", ".pyd", ".so", ".dll", ".dylib", @@ -40,6 +46,12 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) { return false; } +// Add the missing get_relative_path implementation +std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::path& base) { + fs::path rel = fs::relative(path, base); + return rel.string(); +} + std::vector TreeStructurer::get_filtered_paths(const fs::path& start) { std::vector paths; fs::directory_options options = fs::directory_options::skip_permission_denied; @@ -51,7 +63,6 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) for (const auto& entry : fs::recursive_directory_iterator(start, options)) { const auto& path = entry.path(); - // Check if any parent directory should be ignored bool should_skip = false; for (const auto& component : path) { if (should_ignore_dir(component.string())) { @@ -62,11 +73,11 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) if (should_skip) continue; if (entry.is_directory()) { - if (should_ignore_dir(path.filename().string())) { + if (!should_ignore_dir(path.filename().string())) { paths.push_back(path); } } else { - if (should_ignore_file(path.filename().string())) { + if (!should_ignore_file(path.filename().string())) { paths.push_back(path); } } @@ -80,55 +91,47 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) return paths; } -std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::path& base) { - fs::path rel = fs::relative(path, base); - return rel.string(); -} - std::vector TreeStructurer::get_directory_structure(const std::string& startpath) { std::vector result; - fs::path start = startpath.empty() ? fs::current_path() : fs::path(startpath); + + // 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); + } + + fs::path start = normalized_path.empty() ? fs::current_path() : fs::path(normalized_path); try { auto paths = get_filtered_paths(start); if (paths.empty()) return result; - // Don't add the root directory name if we're in the current directory - if (start != fs::current_path()) { - result.push_back(start.filename().string()); - } + // Don't add the start directory name to the output + // Remove the following lines: + // if (start != fs::current_path()) { + // result.push_back(start.filename().string()); + // } std::vector is_last_at_level(256, false); - std::string last_prefix; - + for (size_t i = 1; i < paths.size(); ++i) { const auto& path = paths[i]; std::string rel_path = get_relative_path(path, start); - - // Split the path into components and check each component - bool should_skip = false; - for (const auto& component : fs::path(rel_path)) { - if (should_ignore_dir(component.string())) { - should_skip = true; - break; - } - } - if (should_skip) continue; - + int level = std::count(rel_path.begin(), rel_path.end(), fs::path::preferred_separator); - // Check if this is the last item at this level - bool is_last = false; - for (size_t j = i + 1; j < paths.size(); j++) { - const auto& next_path = paths[j]; - std::string next_rel_path = get_relative_path(next_path, start); + 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 = (j == paths.size() - 1) || (next_level < level); + if (next_level == level) { + is_last = false; + break; + } + if (next_level < level) { break; } } - if (i == paths.size() - 1) is_last = true; is_last_at_level[level] = is_last; @@ -152,4 +155,4 @@ std::vector TreeStructurer::get_directory_structure(const std::stri } return result; -} +} \ No newline at end of file From 9e3910e0f9cff36c937076d720ed8d635a036c0a Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 22:32:48 +0100 Subject: [PATCH 09/10] corrected code to dispaly __init__ and __main__ files as well --- example.py | 6 ++++-- tree_structurer/cpp/tree_structurer.cpp | 11 ++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/example.py b/example.py index c0e7345..4dd9443 100644 --- a/example.py +++ b/example.py @@ -1,3 +1,5 @@ -import tree_structurer +from tree_structurer import create_tree_structurer, get_structure -tree_structurer.get_structure() \ No newline at end of file +create_tree_structurer() +for file in get_structure(): + print(file) \ No newline at end of file diff --git a/tree_structurer/cpp/tree_structurer.cpp b/tree_structurer/cpp/tree_structurer.cpp index 23ff0db..5e35bed 100644 --- a/tree_structurer/cpp/tree_structurer.cpp +++ b/tree_structurer/cpp/tree_structurer.cpp @@ -10,6 +10,11 @@ bool TreeStructurer::should_ignore_dir(const std::string& dirname) { ".git", ".idea", ".vscode", "__pycache__", "**pycache**" }; + // Check for __main__.py or __init__.py files + if (!dirname.empty() && (dirname == "__main__.py" || dirname == "__init__.py")) { + return false; // Do not ignore these files + } + if (std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end()) { return true; } @@ -26,13 +31,17 @@ bool TreeStructurer::should_ignore_dir(const std::string& dirname) { return false; } -// Add the missing should_ignore_file implementation bool TreeStructurer::should_ignore_file(const std::string& filename) { static const std::vector ignore_extensions = { ".pyc", ".pyo", ".pyd", ".so", ".dll", ".dylib", ".o", ".obj", ".a", ".lib" }; + // Check for __main__.py or __init__.py files + if (!filename.empty() && (filename == "__main__.py" || filename == "__init__.py")) { + return false; // Do not ignore these files + } + if (!filename.empty() && (filename[0] == '.' || filename[0] == '_')) { return true; } From 329ac941fd7984a4d2bc6f529eaec3123d07f4c2 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sun, 19 Jan 2025 22:37:25 +0100 Subject: [PATCH 10/10] finalized version 0.0.5 --- README.md | 58 ++++++++++++++++++++++++++++++++++++- tree_structurer/__main__.py | 1 - 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7141c9b..d86c686 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,59 @@ # Tree-Structurer -Gives you an overview of your Project structure \ No newline at end of file +This is a Python package designed to display directory structures in a tree-like format. This tool provides an easy-to-read overview of the directory hierarchy, making it easier for developers to navigate complex project structures. + +#### Installation + +To install `tree_structurer`, you can use pip: + +```bash +pip install pip install git+https://gitea.fabelous.app/Fabel/Tree-Structurer.git + +``` + +#### Usage + +You can run `tree_structurer` from the command line. By default, it will analyze the current directory and print its structure in a tree-like format. You can also specify a path to another directory. + +To display the directory structure of the current directory: + +```bash +python -m tree_structurer +``` + +To display the directory structure of a specific directory: + +```bash +python -m tree_structurer /path/to/directory +``` + +#### Options + +- `-v` or `--verbose`: Show more detailed output, including information about the path being analyzed. + +Example: + +```bash +python -m tree_structurer /path/to/directory -v +``` + +#### Important Files and Folders Ignored + +By default, `tree_structurer` ignores files and folders that start with `_` or `.`. However, it does include the following exceptions: + +- `__init__.py` and `__main__.py` files: These are considered important and will be included in the output. +- Special folders like `build`, `.git`, `node_modules`, etc.: These are also ignored to keep the output focused on the essential parts of the directory structure. + +#### Example Output + +``` +dir Structure for /path/to/directory: +/path/to/directory +├── main.py +├── module1 +│ ├── __init__.py +│ └── submodule1.py +└── utils + ├── helper.py + └── constants.py +``` diff --git a/tree_structurer/__main__.py b/tree_structurer/__main__.py index ba5c985..cb5c6d4 100644 --- a/tree_structurer/__main__.py +++ b/tree_structurer/__main__.py @@ -17,7 +17,6 @@ def main(): create_tree_structurer() # Get and print structure directly structure = get_structure(args.path) - print(f"dir Structure for {args.path}:") for line in structure: print(line)