[PATCH] Descend empty directories in web view, non-optional, optimized

Ry4an Brase ry4an-hg at ry4an.org
Sat Nov 1 01:01:09 CDT 2008


# HG changeset patch
# User Ry4an Brase <ry4an-hg at ry4an.org>
# Date 1224788391 18000
# Node ID 5c2fa1ffb49fa0c95fa046722c8c801bbc27f4cf
# Parent  bd6deb7525f46d66fe1b33b928e5c828218b7f30
Descend empty directories in web view, non-optional, optimized

When a manifest has a series of directories with nothing in them but a
single directory, displaying the entire chain of empty directories
allows for navigation down to the first non-empty directory with a
single click.

Because Java links package hierarchy to directory hierarchy, and because Java
conventions include at least three empty directories at the top of this
hierarchy, this descending of empty directories is par for the course in Java web
tools.

History:

 - Mpm said to make it non-optional
 - Tonfa suggested removing additional folder icons in paper and coal
 - Djc said to:
    - merge previously separate webcommand and template patches
    - make template value a string (not a dict)
    - add tests

Non-interactive sample view:

    http://ry4an.org/tmp/Dime-without-folders.html

diff -r bd6deb7525f4 -r 5c2fa1ffb49f mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py	Thu Oct 30 12:38:24 2008 -0700
+++ b/mercurial/hgweb/webcommands.py	Thu Oct 23 13:59:51 2008 -0500
@@ -264,6 +264,7 @@
     node = ctx.node()
 
     files = {}
+    dirs = {}
     parity = paritygen(web.stripecount)
 
     if path and path[-1] != "/":
@@ -275,20 +276,25 @@
         if f[:l] != path:
             continue
         remain = f[l:]
-        idx = remain.find('/')
-        if idx != -1:
-            remain = remain[:idx+1]
-            n = None
-        files[remain] = (f, n)
+        elements = remain.split('/')
+        if len(elements) == 1:
+            files[remain] = f
+        else:
+            h = dirs # need to retain ref to dirs (root)
+            for elem in elements[0:-1]:
+                if elem not in h:
+                    h[elem] = {}
+                h = h[elem]
+                if len(h) > 1:
+                    break
+            h[None] = None # denotes files present
 
-    if not files:
+    if not files and not dirs:
         raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
 
     def filelist(**map):
         for f in util.sort(files):
-            full, fnode = files[f]
-            if not fnode:
-                continue
+            full = files[f]
 
             fctx = ctx.filectx(full)
             yield {"file": full,
@@ -299,14 +305,22 @@
                    "permissions": mf.flags(full)}
 
     def dirlist(**map):
-        for f in util.sort(files):
-            full, fnode = files[f]
-            if fnode:
-                continue
+        for d in util.sort(dirs):
 
+            emptydirs = []
+            h = dirs[d]
+            while isinstance(h, dict) and len(h) == 1:
+                k,v = h.items()[0]
+                if v:
+                    emptydirs.append(k)
+                h = v
+
+            path = "%s%s" % (abspath, d)
             yield {"parity": parity.next(),
-                   "path": "%s%s" % (abspath, f),
-                   "basename": f[:-1]}
+                   "path": path,
+                   "emptydirs": (),
+                   "emptydirs": "/".join(emptydirs),
+                   "basename": d}
 
     return tmpl("manifest",
                 rev=ctx.rev(),
diff -r bd6deb7525f4 -r 5c2fa1ffb49f templates/coal/map
--- a/templates/coal/map	Thu Oct 30 12:38:24 2008 -0700
+++ b/templates/coal/map	Thu Oct 23 13:59:51 2008 -0500
@@ -23,7 +23,7 @@
 changeset = changeset.tmpl
 manifest = manifest.tmpl
 
-direntry = '<tr class="fileline parity{parity}"><td class="name"><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}"><img src="{staticurl}coal-folder.png"> {basename|escape}/</a><td class="size"></td><td class="permissions">drwxr-xr-x</td></tr>'
+direntry = '<tr class="fileline parity{parity}"><td class="name"><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}"><img src="{staticurl}coal-folder.png"> {basename|escape}/</a> <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a><td class="size"></td><td class="permissions">drwxr-xr-x</td></tr>'
 fileentry = '<tr class="fileline parity{parity}"><td class="filename"><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l1"><img src="{staticurl}coal-file.png"> {basename|escape}</a></td><td class="size">{size}</td><td class="permissions">{permissions|permissions}</td></tr>'
 
 filerevision = filerevision.tmpl
diff -r bd6deb7525f4 -r 5c2fa1ffb49f templates/gitweb/map
--- a/templates/gitweb/map	Thu Oct 30 12:38:24 2008 -0700
+++ b/templates/gitweb/map	Thu Oct 23 13:59:51 2008 -0500
@@ -19,7 +19,7 @@
 searchentry = changelogentry.tmpl
 changeset = changeset.tmpl
 manifest = manifest.tmpl
-direntry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a></td></tr>'
+direntry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a> <a href="#url#file/#node|short##path|urlescape#/#emptydirs|urlescape#{sessionvars%urlparameter}">#emptydirs|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">files</a></td></tr>'
 fileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#date|isodate#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="#url#log/#node|short#/#file|urlescape#{sessionvars%urlparameter}">revisions</a> | <a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a></td></tr>'
 filerevision = filerevision.tmpl
 fileannotate = fileannotate.tmpl
diff -r bd6deb7525f4 -r 5c2fa1ffb49f templates/map
--- a/templates/map	Thu Oct 30 12:38:24 2008 -0700
+++ b/templates/map	Thu Oct 23 13:59:51 2008 -0500
@@ -19,7 +19,7 @@
 searchentry = changelogentry.tmpl
 changeset = changeset.tmpl
 manifest = manifest.tmpl
-direntry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a>'
+direntry = '<tr class="parity#parity#"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a> <a href="#url#file/#node|short##path|urlescape#/#emptydirs|urlescape#{sessionvars%urlparameter}">#emptydirs|urlescape#</a>'
 fileentry = '<tr class="parity#parity#"><td><tt>#permissions|permissions#</tt>&nbsp;<td align=right><tt class="date">#date|isodate#</tt>&nbsp;<td align=right><tt>#size#</tt>&nbsp;<td><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a>'
 filerevision = filerevision.tmpl
 fileannotate = fileannotate.tmpl
diff -r bd6deb7525f4 -r 5c2fa1ffb49f templates/paper/map
--- a/templates/paper/map	Thu Oct 30 12:38:24 2008 -0700
+++ b/templates/paper/map	Thu Oct 23 13:59:51 2008 -0500
@@ -23,7 +23,7 @@
 changeset = ../coal/changeset.tmpl
 manifest = ../coal/manifest.tmpl
 
-direntry = '<tr class="fileline parity{parity}"><td class="name"><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}"><img src="{staticurl}coal-folder.png"> {basename|escape}/</a><td class="size"></td><td class="permissions">drwxr-xr-x</td></tr>'
+direntry = '<tr class="fileline parity{parity}"><td class="name"><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}"><img src="{staticurl}coal-folder.png"> {basename|escape}/</a> <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a><td class="size"></td><td class="permissions">drwxr-xr-x</td></tr>'
 fileentry = '<tr class="fileline parity{parity}"><td class="filename"><a href="{url}file/{node|short}/{file|urlescape}#l1{sessionvars%urlparameter}"><img src="{staticurl}coal-file.png"> {basename|escape}</a></td><td class="size">{size}</td><td class="permissions">{permissions|permissions}</td></tr>'
 
 filerevision = ../coal/filerevision.tmpl
diff -r bd6deb7525f4 -r 5c2fa1ffb49f tests/test-hgweb-descend-empties
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgweb-descend-empties	Thu Oct 23 13:59:51 2008 -0500
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Test chains of near empty directories, terminating 3 different ways:
+# - a1: file at level 4 (deepest)
+# - b1: two dirs at level 3
+# - e1: file at level 2
+
+echo % Set up the repo
+hg init test
+cd test
+mkdir -p a1/a2/a3/a4
+mkdir -p b1/b2/b3/b4
+mkdir -p b1/b2/c3/c4
+mkdir -p d1/d2/d3/d4
+echo foo > a1/a2/a3/a4/foo
+echo foo > b1/b2/b3/b4/foo
+echo foo > b1/b2/c3/c4/foo
+echo foo > d1/d2/d3/d4/foo
+echo foo > d1/d2/foo
+hg ci -d'0 0' -Ambase
+hg serve -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
+cat hg.pid >> $DAEMON_PIDS
+
+echo % File-related
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file'
+
+echo % ERRORS ENCOUNTERED
+cat errors.log
diff -r bd6deb7525f4 -r 5c2fa1ffb49f tests/test-hgweb-descend-empties.out
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgweb-descend-empties.out	Thu Oct 23 13:59:51 2008 -0500
@@ -0,0 +1,51 @@
+% Set up the repo
+adding a1/a2/a3/a4/foo
+adding b1/b2/b3/b4/foo
+adding b1/b2/c3/c4/foo
+adding d1/d2/d3/d4/foo
+adding d1/d2/foo
+% File-related
+200 Script output follows
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<link rel="icon" href="/static/hgicon.png" type="image/png">
+<meta name="robots" content="index, nofollow" />
+<link rel="stylesheet" href="/static/style.css" type="text/css" />
+
+<title>test: files for changeset 8ec8269799d6</title>
+</head>
+<body>
+
+<div class="buttons">
+<a href="/log/0">changelog</a>
+<a href="/shortlog/0">shortlog</a>
+<a href="/graph">graph</a>
+<a href="/tags">tags</a>
+<a href="/rev/8ec8269799d6">changeset</a>
+
+</div>
+
+<h2>files for changeset 8ec8269799d6: /</h2>
+
+<table cellpadding="0" cellspacing="0">
+<tr class="parity0">
+  <td><tt>drwxr-xr-x</tt>&nbsp;
+  <td>&nbsp;
+  <td>&nbsp;
+  <td><a href="/file/8ec8269799d6/">[up]</a>
+</tr>
+<tr class="parity1"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="/file/8ec8269799d6/a1">a1/</a> <a href="/file/8ec8269799d6/a1/a2/a3/a4">a2/a3/a4</a><tr class="parity0"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="/file/8ec8269799d6/b1">b1/</a> <a href="/file/8ec8269799d6/b1/b2">b2</a><tr class="parity1"><td><tt>drwxr-xr-x</tt>&nbsp;<td>&nbsp;<td>&nbsp;<td><a href="/file/8ec8269799d6/d1">d1/</a> <a href="/file/8ec8269799d6/d1/d2">d2</a>
+
+</table>
+
+<div class="logo">
+<a href="http://www.selenic.com/mercurial/">
+<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+</div>
+
+</body>
+</html>
+
+% ERRORS ENCOUNTERED


More information about the Mercurial-devel mailing list