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();
}
|