shithub: hugo

Download patch

ref: 1648e327c0bc7d6535667020a278b355d26a4338
parent: 819271cb78df6853b4c128c3899d57e3812c3078
author: Sven Dowideit <SvenDowideit@home.org.au>
date: Tue Mar 15 12:00:36 EDT 2016

Document and clean SourceRelativeLinksEval code

--- a/docs/content/overview/configuration.md
+++ b/docs/content/overview/configuration.md
@@ -289,6 +289,38 @@
 <td class="purpose-description" colspan="2">Extensions in this option won't be loaded.<br>
 <small><strong>Example:</strong>&nbsp;Add <code>"autoHeaderIds"</code> to disable <code>EXTENSION_AUTO_HEADER_IDS</code>.</small></td>
 </tr>
+
+<tr>
+<td><code><strong>sourceRelativeLinksEval</strong></code></td>
+<td><code>false</code></td>
+<td><code>none</code></td>
+</tr>
+<tr>
+<td class="purpose-title">Purpose:</td>
+<td class="purpose-description" colspan="2">Source file based relative linking (a la Github).<br>
+Relative links to markdown and static files within a page will be evaluated relative to the
+location of that page, and then converted to html links during rendering. For example,
+`[example](../other/page.md)` in `content/total/overview.md` will be linked to
+`content/other/overview.md`, and then rendered to `/other/overview/` in the HTML output.
+</td>
+</tr>
+
+<tr>
+<td><code><strong>sourceRelativeLinksProjectFolder</strong></code></td>
+<td><code>"/docs/content"</code></td>
+<td><code>none</code></td>
+</tr>
+<tr>
+<td class="purpose-title">Purpose:</td>
+<td class="purpose-description" colspan="2">Source file based relative linking Hugo Project sub-folder.<br>
+When `sourceRelativeLinksEval` is enabled, source level paths may contain an absolute respository path to the
+markdown or static file which needs to be removed before trying to match it with the intended link.
+ For example, if your documentation is in `/docs/content`, then
+`[example](/docs/content/other/page.md)` in `/docs/content/total/overview.md` will be linked to
+`/docs/content/other/overview.md`, and then rendered to `/other/overview/` in the HTML output.
+</td>
+</tr>
+
 </tbody>
 </table>
 
--- a/helpers/content.go
+++ b/helpers/content.go
@@ -43,29 +43,31 @@
 
 // Blackfriday holds configuration values for Blackfriday rendering.
 type Blackfriday struct {
-	Smartypants             bool
-	AngledQuotes            bool
-	Fractions               bool
-	HrefTargetBlank         bool
-	SmartDashes             bool
-	LatexDashes             bool
-	PlainIDAnchors          bool
-	SourceRelativeLinksEval bool
-	Extensions              []string
-	ExtensionsMask          []string
+	Smartypants                      bool
+	AngledQuotes                     bool
+	Fractions                        bool
+	HrefTargetBlank                  bool
+	SmartDashes                      bool
+	LatexDashes                      bool
+	PlainIDAnchors                   bool
+	SourceRelativeLinksEval          bool
+	SourceRelativeLinksProjectFolder string
+	Extensions                       []string
+	ExtensionsMask                   []string
 }
 
 // NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
 func NewBlackfriday() *Blackfriday {
 	combinedParam := map[string]interface{}{
-		"smartypants":         true,
-		"angledQuotes":        false,
-		"fractions":           true,
-		"hrefTargetBlank":     false,
-		"smartDashes":         true,
-		"latexDashes":         true,
-		"plainIDAnchors":      false,
-		"sourceRelativeLinks": false,
+		"smartypants":                      true,
+		"angledQuotes":                     false,
+		"fractions":                        true,
+		"hrefTargetBlank":                  false,
+		"smartDashes":                      true,
+		"latexDashes":                      true,
+		"plainIDAnchors":                   false,
+		"sourceRelativeLinks":              false,
+		"sourceRelativeLinksProjectFolder": "/docs/content",
 	}
 
 	siteParam := viper.GetStringMap("blackfriday")
--- a/helpers/content_renderer.go
+++ b/helpers/content_renderer.go
@@ -49,6 +49,7 @@
 		// Use the blackfriday built in Link handler
 		renderer.Renderer.Link(out, link, title, content)
 	} else {
+		// set by SourceRelativeLinksEval
 		newLink, err := renderer.LinkResolver(string(link))
 		if err != nil {
 			newLink = string(link)
@@ -62,6 +63,7 @@
 		// Use the blackfriday built in Image handler
 		renderer.Renderer.Image(out, link, title, alt)
 	} else {
+		// set by SourceRelativeLinksEval
 		newLink, err := renderer.FileResolver(string(link))
 		if err != nil {
 			newLink = string(link)
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -257,10 +257,10 @@
 	var fileFn helpers.FileResolverFunc
 	if p.getRenderingConfig().SourceRelativeLinksEval {
 		fn = func(ref string) (string, error) {
-			return p.Node.Site.GitHub(ref, p)
+			return p.Node.Site.SourceRelativeLink(ref, p)
 		}
 		fileFn = func(ref string) (string, error) {
-			return p.Node.Site.GitHubFileLink(ref, p)
+			return p.Node.Site.SourceRelativeLinkFile(ref, p)
 		}
 	}
 	return helpers.RenderBytes(
@@ -273,10 +273,10 @@
 	var fileFn helpers.FileResolverFunc
 	if p.getRenderingConfig().SourceRelativeLinksEval {
 		fn = func(ref string) (string, error) {
-			return p.Node.Site.GitHub(ref, p)
+			return p.Node.Site.SourceRelativeLink(ref, p)
 		}
 		fileFn = func(ref string) (string, error) {
-			return p.Node.Site.GitHubFileLink(ref, p)
+			return p.Node.Site.SourceRelativeLinkFile(ref, p)
 		}
 	}
 	return helpers.RenderBytesWithTOC(&helpers.RenderingContext{Content: content, PageFmt: p.guessMarkupType(),
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -225,28 +225,18 @@
 	return s.refLink(ref, page, true)
 }
 
-//  TODO(sven): Document
-func (s *SiteInfo) GitHub(ref string, page *Page) (string, error) {
-	return s.githubLink(ref, page, true)
-}
-
-func (s *SiteInfo) githubLink(ref string, currentPage *Page, relative bool) (string, error) {
+// SourceRelativeLink attempts to convert any source page relative links (like [../another.md]) into absolute links
+func (s *SiteInfo) SourceRelativeLink(ref string, currentPage *Page) (string, error) {
 	var refURL *url.URL
 	var err error
 
-	// TODO can I make this a param to `hugo --use-github-links=/docs`?
-	// SVEN: add more tests - the prefix might be a real dir inside tho - add some pages that have it as a legitimate path
-	repositoryPathPrefix := "/docs"
-
-	refURL, err = url.Parse(strings.TrimPrefix(ref, repositoryPathPrefix))
+	refURL, err = url.Parse(strings.TrimPrefix(ref, currentPage.getRenderingConfig().SourceRelativeLinksProjectFolder))
 	if err != nil {
 		return "", err
 	}
 
 	if refURL.Scheme != "" {
-		// TODO: consider looking for http(s?)://github.com/user/project/prefix and replacing it - tho this may be intentional, so idk
-		//return "", fmt.Errorf("Not a plain filepath link (%s)", ref)
-		// Treat this as not an error, as the link is used as-is
+		// Not a relative source level path
 		return ref, nil
 	}
 
@@ -291,12 +281,7 @@
 			return "", fmt.Errorf("No page found for \"%s\" on page \"%s\".\n", ref, currentPage.Source.Path())
 		}
 
-		// SVEN: look at filepath.Rel() it might help, got the rel/non-rel url's (dangerous tho)
-		if relative {
-			link, err = target.RelPermalink()
-		} else {
-			link, err = target.Permalink()
-		}
+		link, err = target.RelPermalink()
 
 		if err != nil {
 			return "", err
@@ -303,7 +288,6 @@
 		}
 	}
 
-	// SVEN: add tests for github style relative fragments
 	if refURL.Fragment != "" {
 		link = link + "#" + refURL.Fragment
 
@@ -317,29 +301,18 @@
 	return link, nil
 }
 
-// TODO(sven): Document
-func (s *SiteInfo) GitHubFileLink(ref string, page *Page) (string, error) {
-	return s.githubFileLink(ref, page, false)
-}
-
-// for non-pages in the site tree
-func (s *SiteInfo) githubFileLink(ref string, currentPage *Page, relative bool) (string, error) {
+// SourceRelativeLinkFile attempts to convert any non-md source relative links (like [../another.gif]) into absolute links
+func (s *SiteInfo) SourceRelativeLinkFile(ref string, currentPage *Page) (string, error) {
 	var refURL *url.URL
 	var err error
 
-	// TODO can I make this a param to `hugo --use-github-links=/docs`?
-	// SVEN: add more tests - the prefix might be a real dir inside tho - add some pages that have it as a legitimate path
-	repositoryPathPrefix := "/docs"
-
-	refURL, err = url.Parse(strings.TrimPrefix(ref, repositoryPathPrefix))
+	refURL, err = url.Parse(strings.TrimPrefix(ref, currentPage.getRenderingConfig().SourceRelativeLinksProjectFolder))
 	if err != nil {
 		return "", err
 	}
 
 	if refURL.Scheme != "" {
-		// TODO: consider looking for http(s?)://github.com/user/project/prefix and replacing it - tho this may be intentional, so idk
-		//return "", fmt.Errorf("Not a plain filepath link (%s)", ref)
-		// Treat this as not an error, as the link is used as-is
+		// Not a relative source level path
 		return ref, nil
 	}
 
@@ -369,13 +342,7 @@
 		}
 
 		link = target.Path()
-		// SVEN: look at filepath.Rel() it might help, got the rel/non-rel url's (dangerous tho)
-		// SVEN: reconsider the fact I hardcoded the `relative` bool in both github resolvers
-		if relative {
-			return "./" + filepath.ToSlash(link), nil
-		} else {
-			return "/" + filepath.ToSlash(link), nil
-		}
+		return "/" + filepath.ToSlash(link), nil
 	}
 
 	return "", fmt.Errorf("failed to find a file to match \"%s\" on page \"%s\"", ref, currentPage.Source.Path())
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -1023,8 +1023,6 @@
 }
 
 func findPage(site *Site, f string) *Page {
-	// TODO: it seems that filepath.FromSlash results in page.Source.Path() returning windows backslash - which means refLinking's string compare is totally busted.
-	// TODO: Not used for non-fragment linking (SVEN thinks this is a bug)
 	currentPath := source.NewFile(filepath.FromSlash(f))
 	//t.Logf("looking for currentPath: %s", currentPath.Path())
 
@@ -1062,6 +1060,15 @@
 		{filepath.FromSlash("level2/level3/common.png"), []byte("")},
 	}
 
+	viper.Set("baseurl", "http://auth/")
+	viper.Set("DefaultExtension", "html")
+	viper.Set("UglyURLs", false)
+	viper.Set("PluralizeListTitles", false)
+	viper.Set("CanonifyURLs", false)
+	viper.Set("blackfriday",
+		map[string]interface{}{
+			"sourceRelativeLinksProjectFolder": "/docs"})
+
 	site := &Site{
 		Source: &source.InMemorySource{ByteSource: sources},
 	}
@@ -1072,12 +1079,6 @@
 		t.Fatalf("Unable to create pages: %s", err)
 	}
 
-	viper.Set("baseurl", "http://auth/bub")
-	viper.Set("DefaultExtension", "html")
-	viper.Set("UglyURLs", false)
-	viper.Set("PluralizeListTitles", false)
-	viper.Set("CanonifyURLs", false)
-
 	return site
 }
 
@@ -1228,7 +1229,7 @@
 			t.Fatalf("failed to find current page in site")
 		}
 		for link, url := range results {
-			if out, err := site.Info.githubLink(link, currentPage, true); err != nil || out != url {
+			if out, err := site.Info.SourceRelativeLink(link, currentPage); err != nil || out != url {
 				t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err)
 			} else {
 				//t.Logf("tested ok %s maps to %s", link, out)
@@ -1241,7 +1242,7 @@
 
 }
 
-func TestGitHubFileLinking(t *testing.T) {
+func TestSourceRelativeLinkFileing(t *testing.T) {
 	viper.Reset()
 	defer viper.Reset()
 	site := setupLinkingMockSite(t)
@@ -1278,7 +1279,7 @@
 			t.Fatalf("failed to find current page in site")
 		}
 		for link, url := range results {
-			if out, err := site.Info.githubFileLink(link, currentPage, false); err != nil || out != url {
+			if out, err := site.Info.SourceRelativeLinkFile(link, currentPage); err != nil || out != url {
 				t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err)
 			} else {
 				//t.Logf("tested ok %s maps to %s", link, out)
@@ -1285,8 +1286,4 @@
 			}
 		}
 	}
-	// TODO: and then the failure cases.
-	// 			"https://docker.com":           "",
-	// site_test.go:1094: Expected https://docker.com to resolve to (), got () - error: Not a plain filepath link (https://docker.com)
-
 }