Compress some relative links in Markdown Link to files in the same directory or any subdirectory using just the relative file path instead of the absolute path. This helps to shrink index.md pages with many links to sibilings by avoiding repetition of the repository name, revision and parent directory path. Change-Id: I34fbdd3f7237ab4637c4e2c3d9cfe005ad535287
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java index da9138e..30470f8 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
@@ -495,7 +495,7 @@ "breadcrumbs", view.getBreadcrumbs(wr.hasSingleTree), "type", FileType.TREE.toString(), "data", - new TreeSoyData(wr.getObjectReader(), view, cfg, wr.root) + new TreeSoyData(wr.getObjectReader(), view, cfg, wr.root, req.getRequestURI()) .setArchiveFormat(getArchiveFormat(getAccess(req))) .toSoyData(wr.id, wr.tw))); }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java b/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java index 44024b0..3dcf1ea 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
@@ -39,15 +39,22 @@ private final GitilesView view; private final MarkdownConfig config; private final RevTree rootTree; + private final String requestUri; private String readmePath; private ObjectId readmeId; - ReadmeHelper(ObjectReader reader, GitilesView view, MarkdownConfig config, RevTree rootTree) { + ReadmeHelper( + ObjectReader reader, + GitilesView view, + MarkdownConfig config, + RevTree rootTree, + String requestUri) { this.reader = reader; this.view = view; this.config = config; this.rootTree = rootTree; + this.requestUri = requestUri; } void scanTree(RevTree tree) @@ -86,6 +93,7 @@ return MarkdownToHtml.builder() .setConfig(config) .setGitilesView(view) + .setRequestUri(requestUri) .setFilePath(readmePath) .setReader(reader) .setRootTree(rootTree)
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java index a6758a9..fee1507 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
@@ -99,7 +99,7 @@ if (headId != null) { RevObject head = walk.parseAny(headId); int limit = LOG_LIMIT; - Map<String, Object> readme = renderReadme(walk, view, access.getConfig(), head); + Map<String, Object> readme = renderReadme(req, walk, view, access.getConfig(), head); if (readme != null) { data.putAll(readme); limit = LOG_WITH_README_LIMIT; @@ -157,7 +157,8 @@ } private static Map<String, Object> renderReadme( - RevWalk walk, GitilesView view, Config cfg, RevObject head) throws IOException { + HttpServletRequest req, RevWalk walk, GitilesView view, Config cfg, RevObject head) + throws IOException { RevTree rootTree; try { rootTree = walk.parseTree(head); @@ -170,7 +171,8 @@ walk.getObjectReader(), GitilesView.path().copyFrom(view).setRevision(Revision.HEAD).setPathPart("/").build(), MarkdownConfig.get(cfg), - rootTree); + rootTree, + req.getRequestURI()); readme.scanTree(rootTree); if (readme.isPresent()) { SanitizedContent html = readme.render();
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java index 9c7fdb0..cc80490 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/RevisionServlet.java
@@ -105,7 +105,9 @@ break; case OBJ_TREE: Map<String, Object> tree = - new TreeSoyData(walk.getObjectReader(), view, cfg, (RevTree) obj).toSoyData(obj); + new TreeSoyData( + walk.getObjectReader(), view, cfg, (RevTree) obj, req.getRequestURI()) + .toSoyData(obj); soyObjects.add(ImmutableMap.of("type", Constants.TYPE_TREE, "data", tree)); hasReadme = tree.containsKey("readmeHtml"); break;
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/TreeSoyData.java b/gitiles-servlet/src/main/java/com/google/gitiles/TreeSoyData.java index 056ae24..43fddfb 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/TreeSoyData.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/TreeSoyData.java
@@ -73,13 +73,16 @@ private final GitilesView view; private final Config cfg; private final RevTree rootTree; + private final String requestUri; private ArchiveFormat archiveFormat; - public TreeSoyData(ObjectReader reader, GitilesView view, Config cfg, RevTree rootTree) { + public TreeSoyData( + ObjectReader reader, GitilesView view, Config cfg, RevTree rootTree, String requestUri) { this.reader = reader; this.view = view; this.cfg = cfg; this.rootTree = rootTree; + this.requestUri = requestUri; } public TreeSoyData setArchiveFormat(ArchiveFormat archiveFormat) { @@ -89,7 +92,8 @@ public Map<String, Object> toSoyData(ObjectId treeId, TreeWalk tw) throws MissingObjectException, IOException { - ReadmeHelper readme = new ReadmeHelper(reader, view, MarkdownConfig.get(cfg), rootTree); + ReadmeHelper readme = + new ReadmeHelper(reader, view, MarkdownConfig.get(cfg), rootTree, requestUri); List<Object> entries = Lists.newArrayList(); GitilesView.Builder urlBuilder = GitilesView.path().copyFrom(view); while (tw.next()) {
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java index e669014..67bf2c2 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
@@ -127,6 +127,7 @@ MarkdownToHtml.builder() .setConfig(cfg) .setGitilesView(view) + .setRequestUri(req.getRequestURI()) .setReader(reader) .setRootTree(root); res.setHeader(HttpHeaders.ETAG, curEtag);
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java index ae78862..7a116c1 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/MarkdownToHtml.java
@@ -80,6 +80,7 @@ } public static class Builder { + private String requestUri; private GitilesView view; private MarkdownConfig config; private String filePath; @@ -88,6 +89,11 @@ Builder() {} + public Builder setRequestUri(@Nullable String uri) { + requestUri = uri; + return this; + } + public Builder setGitilesView(@Nullable GitilesView view) { this.view = view; return this; @@ -120,6 +126,7 @@ private final HtmlBuilder html = new HtmlBuilder(); private final TocFormatter toc = new TocFormatter(html, 3); + private final String requestUri; private final GitilesView view; private final MarkdownConfig config; private final String filePath; @@ -127,6 +134,7 @@ private boolean outputNamedAnchor = true; private MarkdownToHtml(Builder b) { + requestUri = b.requestUri; view = b.view; config = b.config; filePath = b.filePath; @@ -378,7 +386,9 @@ } else { b = GitilesView.path(); } - return b.copyFrom(view).setPathPart(dest).build().toUrl() + anchor; + dest = b.copyFrom(view).setPathPart(dest).build().toUrl(); + + return PathResolver.relative(requestUri, dest) + anchor; } @Override
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/PathResolver.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/PathResolver.java index 045ecda..1cc8095 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/PathResolver.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/PathResolver.java
@@ -67,5 +67,23 @@ return slash < 0 ? "" : path.substring(0, slash); } + static String relative(@Nullable String requestUri, String dest) { + if (requestUri != null) { + // base is the path the browser will use for relative URLs. + String base = requestUri; + if (!base.endsWith("/")) { + int slash = base.lastIndexOf('/'); + if (slash < 0) { + return dest; + } + base = base.substring(0, slash + 1); + } + if (dest.startsWith(base)) { + return dest.substring(base.length()); + } + } + return dest; + } + private PathResolver() {} }
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/doc/LinkTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/doc/LinkTest.java index 441fe61..fdffffc 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/doc/LinkTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/doc/LinkTest.java
@@ -227,4 +227,24 @@ .setFilePath(file) .build(); } + + @Test + public void automaticRelativePaths() { + MarkdownToHtml md = + MarkdownToHtml.builder() + .setGitilesView(GitilesView.doc().copyFrom(view).setPathPart("docs/index.md").build()) + .setConfig(new MarkdownConfig(config)) + .setFilePath("/docs/index.md") + .setRequestUri("/g/repo/+/HEAD/docs/index.md") + .build(); + + assertThat(md.href("help.md")).isEqualTo("help.md"); + assertThat(md.href("/docs/help.md")).isEqualTo("help.md"); + assertThat(md.href("technical/format.md")).isEqualTo("technical/format.md"); + assertThat(md.href("/docs/technical/format.md")).isEqualTo("technical/format.md"); + + assertThat(md.href("../README.md")).isEqualTo("/g/repo/+/HEAD/README.md"); + assertThat(md.href("../src/catalog.md")).isEqualTo("/g/repo/+/HEAD/src/catalog.md"); + assertThat(md.href("/src/catalog.md")).isEqualTo("/g/repo/+/HEAD/src/catalog.md"); + } }
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/doc/PathResolverTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/doc/PathResolverTest.java index d788f43..f1990ec 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/doc/PathResolverTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/doc/PathResolverTest.java
@@ -15,6 +15,7 @@ package com.google.gitiles.doc; import static com.google.common.truth.Truth.assertThat; +import static com.google.gitiles.doc.PathResolver.relative; import static com.google.gitiles.doc.PathResolver.resolve; import org.junit.Test; @@ -38,4 +39,17 @@ assertThat(resolve("/a/b/c/index.md", "../../foo.md")).isEqualTo("a/foo.md"); assertThat(resolve("/a/index.md", "../../../foo.md")).isNull(); } + + @Test + public void relativeTests() { + assertThat(relative(null, "/g/foo.md")).isEqualTo("/g/foo.md"); + + assertThat(relative("/g", "/g/foo.md")).isEqualTo("g/foo.md"); + assertThat(relative("/r", "/g/foo.md")).isEqualTo("g/foo.md"); + assertThat(relative("/a/b/r", "/a/b/g/foo.md")).isEqualTo("g/foo.md"); + + assertThat(relative("/g/", "/g/foo.md")).isEqualTo("foo.md"); + assertThat(relative("/g/bar.md", "/g/foo.md")).isEqualTo("foo.md"); + assertThat(relative("/g/a/b.md", "/g/foo.md")).isEqualTo("/g/foo.md"); + } }