List for cgit developers and users
 help / color / mirror / Atom feed
From: john at keeping.me.uk (John Keeping)
Subject: Display of directories that contain only one subdirectory
Date: Wed, 13 Jul 2016 20:49:28 +0100	[thread overview]
Message-ID: <20160713194928.ebdeywwvx2vhgyxn@john.keeping.me.uk> (raw)
In-Reply-To: <fc590f0d-3397-ae89-bf5e-d83354ef57ce@gmx.de>

On Wed, Jul 13, 2016 at 07:57:23PM +0200, Erik Brangs wrote:
> I use cgit via websites that use it to provide a repository browser,
> i.e. I don't have my own deployment. IMHO it would be nice to get a
> more comfortable navigation for directories that only contain one
> subdirectory. For example, for a path like "contrib/hooks" at
> https://git.zx2c4.com/cgit/tree/ , you first need to click on
> "contrib" and then again on "hooks". Navigation could be simplified by
> providing a link for "contrib/hooks" that points directly to the
> "contrib/hooks" path.
> 
> GitHub's repository browser implements this feature. For example, at
> https://github.com/torvalds/linux/tree/master/tools , there's a link
> for "kvm/kvm_stat" that points directly to
> "https://github.com/torvalds/linux/tree/master/tools/kvm/kvm_stat" .
> 
> I looked at cgitrc.5.txt and didn't find any evidence that this
> feature was already implemented for cgit. Assuming I didn't miss
> anything, would someone be interested in implementing it?

It turns out it's not too difficult, patch below.  This is slightly
different from Github's behaviour because it allows clicking on
intermediate directories; it wouldn't be hard to collect the full path
instead but I actually prefer this way of doing things.

-- >8 --
Subject: [PATCH] tree: allow skipping through single-child trees

If we have only a single element in a directory (for example in Java
package paths), display multiple directories in one go so that it is
possible to navigate directly to the first directory that contains
either files or multiple directories.

Signed-off-by: John Keeping <john at keeping.me.uk>
---
 ui-tree.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 68 insertions(+), 2 deletions(-)

diff --git a/ui-tree.c b/ui-tree.c
index 120066c..2252490 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -155,6 +155,72 @@ static void print_object(const unsigned char *sha1, char *path, const char *base
 		print_text_buffer(basename, buf, size);
 }
 
+struct single_tree_ctx {
+	struct strbuf *path;
+	unsigned char sha1[GIT_SHA1_RAWSZ];
+	char *name;
+	size_t count;
+};
+
+static int single_tree_cb(const unsigned char *sha1, struct strbuf *base,
+			  const char *pathname, unsigned mode, int stage,
+			  void *cbdata)
+{
+	struct single_tree_ctx *ctx = cbdata;
+
+	if (++ctx->count > 1)
+		return -1;
+
+	if (!S_ISDIR(mode)) {
+		ctx->count = 2;
+		return -1;
+	}
+
+	ctx->name = xstrdup(pathname);
+	memcpy(ctx->sha1, sha1, GIT_SHA1_RAWSZ);
+	strbuf_addf(ctx->path, "/%s", pathname);
+	return 0;
+}
+
+static void write_tree_link(const unsigned char *sha1, char *name,
+			    char *rev, struct strbuf *fullpath)
+{
+	size_t initial_length = fullpath->len;
+	struct tree *tree;
+	struct single_tree_ctx tree_ctx = {
+		.path = fullpath,
+		.count = 1,
+	};
+	struct pathspec paths = {
+		.nr = 0
+	};
+
+	memcpy(tree_ctx.sha1, sha1, GIT_SHA1_RAWSZ);
+
+	while (tree_ctx.count == 1) {
+		cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev,
+			       fullpath->buf);
+
+		tree = lookup_tree(tree_ctx.sha1);
+		if (!tree)
+			return;
+
+		free(tree_ctx.name);
+		tree_ctx.name = NULL;
+		tree_ctx.count = 0;
+
+		read_tree_recursive(tree, "", 0, 1, &paths, single_tree_cb,
+				    &tree_ctx);
+
+		if (tree_ctx.count != 1)
+			break;
+
+		html(" / ");
+		name = tree_ctx.name;
+	}
+
+	strbuf_setlen(fullpath, initial_length);
+}
 
 static int ls_item(const unsigned char *sha1, struct strbuf *base,
 		const char *pathname, unsigned mode, int stage, void *cbdata)
@@ -187,8 +253,8 @@ static int ls_item(const unsigned char *sha1, struct strbuf *base,
 	if (S_ISGITLINK(mode)) {
 		cgit_submodule_link("ls-mod", fullpath.buf, sha1_to_hex(sha1));
 	} else if (S_ISDIR(mode)) {
-		cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
-			       walk_tree_ctx->curr_rev, fullpath.buf);
+		write_tree_link(sha1, name, walk_tree_ctx->curr_rev,
+				&fullpath);
 	} else {
 		char *ext = strrchr(name, '.');
 		strbuf_addstr(&class, "ls-blob");
-- 
2.9.0.465.g8850cbc



  reply	other threads:[~2016-07-13 19:49 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-07-13 17:57 erik.brangs
2016-07-13 19:49 ` john [this message]
2016-07-13 22:12   ` jamie.couture
2016-07-13 22:43     ` jamie.couture
2016-08-07 15:09       ` john
2016-07-14 18:12   ` erik.brangs
2016-08-07 15:12     ` john
2016-08-08 22:48       ` Jason

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160713194928.ebdeywwvx2vhgyxn@john.keeping.me.uk \
    --to=cgit@lists.zx2c4.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).