about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2023-12-16 07:43:48 -0600
committergennyble <gen@nyble.dev>2023-12-16 07:43:48 -0600
commit733a517036bc5ad3327b41f494b0d09cd2470262 (patch)
tree4581a25c2fd60c7e5ef075c6aad788fd1575bfbe
parentfad097a08e55c9eec0fa86efec72de4426674ca2 (diff)
downloadcgit-pink-733a517036bc5ad3327b41f494b0d09cd2470262.tar.gz
cgit-pink-733a517036bc5ad3327b41f494b0d09cd2470262.zip
allow ordering sections with scan-path HEAD master
-rw-r--r--cgit.c6
-rw-r--r--cgit.h3
-rw-r--r--cgitrc.5.txt14
-rw-r--r--ui-repolist.c105
4 files changed, 114 insertions, 14 deletions
diff --git a/cgit.c b/cgit.c
index dd28a79..a501298 100644
--- a/cgit.c
+++ b/cgit.c
@@ -258,7 +258,9 @@ static void config_cb(const char *name, const char *value)
 	else if (!strcmp(name, "repository-sort"))
 		ctx.cfg.repository_sort = xstrdup(value);
 	else if (!strcmp(name, "section-sort"))
-		ctx.cfg.section_sort = atoi(value);
+		ctx.cfg.section_sort = xstrdup(value);
+	else if (!strcmp(name, "section-order"))
+		string_list_append(&ctx.cfg.section_order, xstrdup(value));
 	else if (!strcmp(name, "source-filter"))
 		ctx.cfg.source_filter = cgit_new_filter(value, SOURCE);
 	else if (!strcmp(name, "summary-log"))
@@ -401,7 +403,7 @@ static void prepare_context(void)
 	ctx.cfg.script_name = CGIT_SCRIPT_NAME;
 	ctx.cfg.section = "";
 	ctx.cfg.repository_sort = "name";
-	ctx.cfg.section_sort = 1;
+	ctx.cfg.section_sort = "name";
 	ctx.cfg.summary_branches = 10;
 	ctx.cfg.summary_log = 10;
 	ctx.cfg.summary_tags = 10;
diff --git a/cgit.h b/cgit.h
index 72fcd84..518cefe 100644
--- a/cgit.h
+++ b/cgit.h
@@ -212,6 +212,7 @@ struct cgit_config {
 	char *root_readme;
 	char *script_name;
 	char *section;
+	struct string_list section_order;
 	char *repository_sort;
 	char *virtual_root;	/* Always ends with '/'. */
 	char *strict_export;
@@ -256,7 +257,7 @@ struct cgit_config {
 	int scan_hidden_path;
 	int section_from_path;
 	int snapshots;
-	int section_sort;
+	char *section_sort;
 	int summary_branches;
 	int summary_log;
 	int summary_tags;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index cafb535..4fa3f9a 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -389,10 +389,16 @@ section::
 	none.
 
 section-sort::
-	Flag which, when set to "1", will sort the sections on the repository
-	listing by name. Set this flag to "0" if the order in the cgitrc file should
-	be preserved. Default value: "1". See also: section,
-	case-sensitive-sort, repository-sort.
+	The way in which sections are sorted. Valid values are "name" for sorting
+	by section name or "order" to sort with section-order. Any other value will
+	preserve the order in the cgitrc file. Default value: "name". See also:
+	section, section-order, case-sensitive-sort, repository-sort.
+
+section-order::
+	The name of a directory to put at the top of the repolist. Multiple config
+	keys may be specified and will appear in order. Repositories without a
+	section appear before ordered sections and sections not defined will appear
+	after, sorted by name. See also: section-sort
 
 section-from-path::
 	A number which, if defined prior to scan-path, specifies how many
diff --git a/ui-repolist.c b/ui-repolist.c
index 97b11c5..a46df0e 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -236,6 +236,64 @@ static int sort_section(const void *a, const void *b)
 	return result;
 }
 
+static int string_list_index(const struct string_list *list, const char *str)
+{
+	for (int i = 0; i < list->nr; ++i) {
+		if (strcmp(list->items[i].string, str) == 0)
+			return i;
+	}
+
+	return -1;
+}
+
+static int strempty(const char *str) {
+	if (str == NULL)
+		return 1;
+	
+	return str[0] == 0;
+}
+
+static int sort_section_order(const void *a, const void *b)
+{
+	const struct cgit_repo *r1 = a;
+	const struct cgit_repo *r2 = b;
+	int result;
+
+	int sec1_idx = string_list_index(&ctx.cfg.section_order, r1->section);
+	int sec2_idx = string_list_index(&ctx.cfg.section_order, r2->section);
+	int both_unordered = sec1_idx == -1 && sec2_idx == -1;
+	int both_empty = strempty(r1->section) && strempty(r2->section);
+
+	if (both_empty) {
+		// both sections empty, sort within the emptiness
+		result = 0;
+	} else if (strempty(r1->section)) {
+		return -1;
+	} else if (strempty(r2->section)) {
+		return 1;
+	} else if (both_unordered) {
+		// Both are unordered, compare lexigraphically
+		result = cmp(r1->section, r2->section);
+	} else if (sec1_idx == -1) {
+		return 1;
+	} else if (sec2_idx == -1) {
+		return -1;
+	} else {
+		result = sec1_idx - sec2_idx;
+	}
+
+	if (result == 0) {
+		// sections are equal, sort within them
+		if (strcmp(ctx.cfg.repository_sort, "age") == 0)
+			result = sort_idle(r1, r2);
+
+		if (result == 0)
+			result = cmp(r1->name, r2->name);
+	}
+
+	return result;
+}
+
 struct sortcolumn {
 	const char *name;
 	int (*fn)(const void *a, const void *b);
@@ -243,6 +301,7 @@ struct sortcolumn {
 
 static const struct sortcolumn sortcolumn[] = {
 	{"section", sort_section},
+	{"section_order", sort_section_order},
 	{"name", sort_name},
 	{"desc", sort_desc},
 	{"owner", sort_owner},
@@ -253,12 +312,13 @@ static const struct sortcolumn sortcolumn[] = {
 static int sort_repolist(char *field)
 {
 	const struct sortcolumn *column;
-
 	for (column = &sortcolumn[0]; column->name; column++) {
 		if (strcmp(field, column->name))
 			continue;
+
 		qsort(cgit_repolist.repos, cgit_repolist.count,
 			sizeof(struct cgit_repo), column->fn);
+
 		return 1;
 	}
 	return 0;
@@ -290,46 +350,71 @@ void cgit_print_repolist(void)
 
 	if (ctx.qry.sort)
 		sorted = sort_repolist(ctx.qry.sort);
-	else if (ctx.cfg.section_sort)
+	else if (strcmp(ctx.cfg.section_sort, "name") == 0)
 		sort_repolist("section");
+	else if (strcmp(ctx.cfg.section_sort, "order") == 0)
+		sort_repolist("section_order");
 
 	html("<table summary='repository list' class='list nowrap'>");
 	for (i = 0; i < cgit_repolist.count; i++) {
 		ctx.repo = &cgit_repolist.repos[i];
+
 		if (!is_visible(ctx.repo))
 			continue;
+
 		hits++;
 		if (hits <= ctx.qry.ofs)
 			continue;
+
 		if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
 			continue;
+
 		if (!header++)
 			print_header();
+
 		section = ctx.repo->section;
 		if (section && !strcmp(section, ""))
 			section = NULL;
-		if (!sorted &&
-		    ((last_section == NULL && section != NULL) ||
-		    (last_section != NULL && section == NULL) ||
-		    (last_section != NULL && section != NULL &&
-		     strcmp(section, last_section)))) {
+
+		// condition:
+		// - not sorted AND
+		// - one of them exist, but not both OR
+		// - both exist and are not equal
+		//
+		// (   one of them exists, but not both   )
+		// (!last && section) || (last && !section) || (both exist and are not equal)
+		if (!sorted && (
+				(last_section == NULL && section != NULL) ||
+				(last_section != NULL && section == NULL) ||
+				(last_section != NULL && section != NULL && strcmp(section, last_section))
+		)) {
 			htmlf("<tr class='nohover-highlight'><td colspan='%d' class='reposection'>",
 			      columns);
 			html_txt(section);
 			html("</td></tr>");
 			last_section = section;
 		}
+
 		htmlf("<tr><td class='%s'>",
 		      !sorted && section ? "sublevel-repo" : "toplevel-repo");
+
 		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
+
 		html("</td><td>");
+
 		repourl = cgit_repourl(ctx.repo->url);
+
 		html_link_open(repourl, NULL, NULL);
+
 		free(repourl);
+
 		if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0)
 			html("...");
+
 		html_link_close();
+
 		html("</td><td>");
+
 		if (ctx.cfg.enable_index_owner) {
 			if (ctx.repo->owner_filter) {
 				cgit_open_filter(ctx.repo->owner_filter);
@@ -348,8 +433,10 @@ void cgit_print_repolist(void)
 			}
 			html("</td><td>");
 		}
+
 		print_modtime(ctx.repo);
 		html("</td>");
+
 		if (ctx.cfg.enable_index_links) {
 			html("<td>");
 			cgit_summary_link("summary", NULL, "button", NULL);
@@ -360,11 +447,15 @@ void cgit_print_repolist(void)
 			cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
 			html("</td>");
 		}
+
 		html("</tr>\n");
 	}
+
 	html("</table>");
+
 	if (hits > ctx.cfg.max_repo_count)
 		print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort);
+
 	cgit_print_docend();
 }