add custom fragment indentation default tip

Mon, 19 May 2025 16:05:58 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 19 May 2025 16:05:58 +0200
changeset 52
e9edc3bd0301
parent 51
49fdc2eb7cd4

add custom fragment indentation

src/html.cpp file | annotate | diff | comparison | revisions
src/html.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/repositories.cpp file | annotate | diff | comparison | revisions
src/repositories.h file | annotate | diff | comparison | revisions
src/settings.h file | annotate | diff | comparison | revisions
--- a/src/html.cpp	Mon May 19 15:34:30 2025 +0200
+++ b/src/html.cpp	Mon May 19 16:05:58 2025 +0200
@@ -25,6 +25,7 @@
 #include "html.h"
 
 #include <cstdio>
+#include <cassert>
 
 using namespace std::chrono;
 
@@ -32,9 +33,11 @@
     static constexpr const char* weekdays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
 
     static unsigned indentation;
-    static const char *tabs = "                                ";
+    static const char *tabs = "                                                                ";
     static void indent(int change = 0) {
         indentation += change;
+        assert(indentation >= 0);
+        assert(indentation <= max_indentation);
         fwrite(tabs, 4, indentation, stdout);
     }
 
@@ -67,10 +70,11 @@
     }
 }
 
-void html::open(bool fragment) {
+void html::open(bool fragment, unsigned char fragment_indent) {
     if (fragment) {
+        indent(fragment_indent);
         puts("<div class=\"heatmap-content\">");
-        indentation = 1;
+        indentation++;
     } else {
         puts(R"(<!DOCTYPE html>
 <html>
@@ -128,6 +132,7 @@
 
 void html::close(bool fragment) {
     if (fragment) {
+        indent(-1);
         puts("</div>");
     } else {
         puts("        </div>\n    </body>\n</html>");
--- a/src/html.h	Mon May 19 15:34:30 2025 +0200
+++ b/src/html.h	Mon May 19 16:05:58 2025 +0200
@@ -31,9 +31,11 @@
 
 namespace html {
 
+    static constexpr unsigned max_indentation = 16;
+    static constexpr unsigned max_external_indentation = max_indentation - 4;
     static constexpr unsigned columns = 53;
 
-    void open(bool fragment);
+    void open(bool fragment, unsigned char fragment_indent = 0);
     void close(bool fragment);
 
     void chart_begin(const std::string& repo, const std::string& author);
--- a/src/main.cpp	Mon May 19 15:34:30 2025 +0200
+++ b/src/main.cpp	Mon May 19 16:05:58 2025 +0200
@@ -48,7 +48,7 @@
         "                             (repeat option to report multiple authors)\n"
         "   -A, --authormap <file>    Apply an author mapping file\n"
         "   -d, --depth <num>         The search depth (default: 1, max: 255)\n"
-        "   -f, --fragment            Output as fragment\n"
+        "   -f, --fragment [indent]   Output as fragment\n"
         "   -h, --help                Print this help message\n"
         "   -p, --pull                Try to pull the repositories\n"
         "   -s, --separate            Output a separate heat map for each repository\n"
@@ -86,7 +86,8 @@
         "\033[1m--separate\033[22m option is specified in which case each repository is displayed with\n"
         "its own heat map. By using the \033[1m--fragment\033[22m option, the tool only outputs a\n"
         "single HTML div container without any header or footer that can be embedded in\n"
-        "your custom web page.\n"
+        "your custom web page. You can optionally specify an indentation for that\n"
+        "container (default is 0 and maximum is 12).\n"
         , stderr);
 }
 
@@ -100,10 +101,11 @@
     errno = 0;
     unsigned long d = strtoul(str, &endptr, 10);
     if (*endptr != '\0' || errno == ERANGE) return true;
-    if (d < max) {
+    if (d <= max) {
         *result = d;
         return false;
     } else {
+        errno = ERANGE;
         return true;
     }
 }
@@ -114,7 +116,7 @@
             print_help();
             return 1;
         } else if (chk_arg(argv[i], "-d", "--depth")) {
-            if (i + 1 >= argc || parse_unsigned(argv[++i], &settings.depth, 256)) {
+            if (i + 1 >= argc || parse_unsigned(argv[++i], &settings.depth, 255)) {
                 fputs("missing or invalid depth\n", stderr);
                 return -1;
             }
@@ -134,6 +136,12 @@
             settings.update_repos = true;
         } else if (chk_arg(argv[i], "-f", "--fragment")) {
             settings.fragment = true;
+            if (i + 1 < argc && !parse_unsigned(argv[i+1], &settings.fragment_indent, html::max_external_indentation)) {
+                i++;
+            } else if (errno == ERANGE) {
+                fputs("invalid fragment indentation\n", stderr);
+                return -1;
+            }
         } else if (chk_arg(argv[i], "-s", "--separate")) {
             settings.separate = true;
         } else if (chk_arg(argv[i], "-A", "--authormap")) {
@@ -201,9 +209,19 @@
     // scan for repos
     fm::repositories repos;
     for (auto &&path: settings.paths) {
+        if (!fm::repositories::exists(path)) {
+            fprintf(stderr, "Path '%s' does not exist!\n", path.c_str());
+            return EXIT_FAILURE;
+        }
         repos.scan(path, settings.depth);
     }
 
+    // check if we found something
+    if (repos.count() == 0) {
+        fprintf(stderr, "No repositories found!\n");
+        return EXIT_FAILURE;
+    }
+
     // update repos, if not disabled
     if (settings.update_repos) {
         for (auto &&repo : repos.list()) {
@@ -262,7 +280,7 @@
         }
     }
 
-    html::open(settings.fragment);
+    html::open(settings.fragment, settings.fragment_indent);
     for (const auto &[repo, authors] : heatmap.data()) {
         bool h1_rendered = false;
         for (const auto &[author, entries] : authors) {
--- a/src/repositories.cpp	Mon May 19 15:34:30 2025 +0200
+++ b/src/repositories.cpp	Mon May 19 16:05:58 2025 +0200
@@ -36,6 +36,10 @@
       type(type) {
 }
 
+bool repositories::exists(const std::string &path) {
+    return fs::is_directory(path);
+}
+
 void repositories::scan(std::string path, unsigned depth) {
     // check the base path
     {
--- a/src/repositories.h	Mon May 19 15:34:30 2025 +0200
+++ b/src/repositories.h	Mon May 19 16:05:58 2025 +0200
@@ -25,6 +25,7 @@
 #ifndef REPOSITORIES_H
 #define REPOSITORIES_H
 
+#include <filesystem>
 #include <string>
 #include <vector>
 
@@ -45,10 +46,14 @@
 class repositories final {
     std::vector<repository> m_repositories;
 public:
+    [[nodiscard]] static bool exists(const std::string &path);
     void scan(std::string path, unsigned depth);
     [[nodiscard]] const std::vector<repository>& list() const {
         return m_repositories;
     }
+    [[nodiscard]] size_t count() const {
+        return m_repositories.size();
+    }
 };
 
 } // fm
--- a/src/settings.h	Mon May 19 15:34:30 2025 +0200
+++ b/src/settings.h	Mon May 19 16:05:58 2025 +0200
@@ -45,6 +45,7 @@
     bool update_repos = false;
     bool separate = false;
     bool fragment = false;
+    unsigned char fragment_indent = 0;
     unsigned short year = settings_current_year;
 
     int parse_authormap(const std::string& path);

mercurial